@reblu/site-blocks 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/hooks/use-collection.ts","../src/components/collection.tsx","../src/components/share-buttons.tsx","../src/lib/share-urls.ts","../src/components/add-to-cart-button.tsx","../src/components/cart-icon.tsx","../src/components/product-carousel.tsx","../src/components/_heading.tsx","../src/components/product-card.tsx","../src/lib/format-price.ts","../src/components/course-syllabus.tsx","../src/lib/slot-class-name.ts","../src/components/data-request-form.tsx","../src/components/withdrawal-request-form.tsx","../src/components/course-guest-checkout-form.tsx","../src/lib/course-guest-checkout.ts","../src/components/checkout-pay-action.tsx","../src/components/checkout-terms-acceptance.tsx","../src/components/full-invoice-fields.tsx"],"sourcesContent":["'use client'\n\n// `@reblu/site-blocks/client` — the client-only surface. Every export here needs\n// browser state or hooks, so the entry carries the `'use client'` directive and\n// each SITE consumes it from a Client Component boundary.\n\nexport {\n useCollection,\n type CollectionFilter,\n type UseCollectionOptions,\n type UseCollectionResult,\n} from './hooks/use-collection'\n\nexport { Collection } from './components/collection'\nexport type {\n CollectionProps,\n CollectionFacet,\n CollectionFacetOption,\n CollectionLabels,\n} from './components/collection'\n\nexport { ShareButtons } from './components/share-buttons'\nexport type { ShareButtonsProps, ShareButtonsLabels } from './components/share-buttons'\n\n// ── Cart feedback (REB-837) ──────────────────────────────────────────────────\n// `AddToCartButton`/`CartIcon` moved here from the RSC-safe root: both now\n// carry real internal state (confirmed/error timer, bump-on-add guard).\nexport {\n AddToCartButton,\n type AddToCartButtonProps,\n type AddToCartButtonLabels,\n type AddToCartButtonStatus,\n} from './components/add-to-cart-button'\nexport { CartIcon, type CartIconProps, type CartIconLabels } from './components/cart-icon'\n\n// ── Product carousel (REB-1120) ──────────────────────────────────────────────\n// Scroll-snap strip with prev/next arrows for «Productos relacionados»; it\n// tracks the scroll position, so it is client-only.\nexport {\n ProductCarousel,\n DEFAULT_PRODUCT_CAROUSEL_LABELS,\n type ProductCarouselProps,\n type ProductCarouselDefaultCardProps,\n type ProductCarouselCustomCardProps,\n type ProductCarouselLabels,\n} from './components/product-carousel'\n\n// ── Course programme (REB-1134) ──────────────────────────────────────────────\n// Expandable module → lesson list over `AcademyCourseDetail.modules`; it holds\n// each module's open/closed state, so it is client-only.\nexport {\n CourseSyllabus,\n DEFAULT_COURSE_SYLLABUS_LABELS,\n type CourseSyllabusProps,\n type CourseSyllabusModule,\n type CourseSyllabusLabels,\n} from './components/course-syllabus'\n\n// ── GDPR rights form (REB-980) ───────────────────────────────────────────────\n// Drop-in public form over `POST /api/site/data-requests` (REB-77).\nexport {\n DataRequestForm,\n DATA_REQUEST_TYPES,\n DATA_REQUEST_FIELD_LIMITS,\n DEFAULT_DATA_REQUEST_FORM_LABELS,\n type DataRequestFormProps,\n type DataRequestFormLabels,\n type DataRequestSubmitResult,\n} from './components/data-request-form'\n\n// ── Withdrawal form (REB-1201) ───────────────────────────────────────────────\n// Drop-in form over `POST /api/site/withdrawal-requests`.\nexport {\n WithdrawalRequestForm,\n WITHDRAWAL_REQUEST_FIELD_LIMITS,\n DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS,\n type WithdrawalRequestFormProps,\n type WithdrawalRequestFormLabels,\n type WithdrawalRequestSubmitResult,\n} from './components/withdrawal-request-form'\n\n// ── Guest course checkout (REB-1264) ─────────────────────────────────────────\n// Drop-in one-step checkout over `POST /api/site/academy/checkout` for a\n// course that needs no validation; gives way to the request form otherwise.\nexport {\n CourseGuestCheckoutForm,\n DEFAULT_COURSE_GUEST_CHECKOUT_FORM_LABELS,\n DEFAULT_COURSE_GUEST_CHECKOUT_ERROR_MESSAGES,\n type CourseGuestCheckoutFormProps,\n type CourseGuestCheckoutFormLabels,\n type CourseGuestCheckoutCopy,\n type CourseGuestCheckoutInput,\n type CourseGuestCheckoutResult,\n} from './components/course-guest-checkout-form'\n","'use client'\n\nimport { useCallback, useMemo, useState } from 'react'\n\n/**\n * `useCollection` — the headless heart of `@reblu/site-blocks`.\n *\n * A framework-agnostic, presentation-free hook that turns an in-memory array into\n * a filtered / searched / sorted / paginated view. It backs BOTH the page index\n * and the blog index: the facets differ (category, tag, …), the machinery does\n * not. Callers own how items look (`Collection` or their own markup); this hook\n * only owns *which* items and in *what* order.\n *\n * All derivation is memoised and pure — comparators run over `toSorted`, never\n * mutating the caller's array.\n */\n\n/** A named facet: `key` identifies it, `predicate` decides membership for a value. */\nexport interface CollectionFilter<T> {\n key: string\n predicate: (item: T, value: string) => boolean\n}\n\nexport interface UseCollectionOptions<T> {\n items: T[]\n /** Page size. `0`/omitted → no pagination (one page with everything). */\n pageSize?: number\n initialPage?: number\n filters?: CollectionFilter<T>[]\n initialFilters?: Record<string, string | undefined>\n /** Free-text matcher; only applied when a non-empty term is set. */\n search?: (item: T, term: string) => boolean\n initialSearch?: string\n /** Named comparators, selected by `sort`. */\n sorts?: Record<string, (a: T, b: T) => number>\n initialSort?: string\n}\n\nexport interface UseCollectionResult<T> {\n /** Items on the current page (or all items when pagination is off). */\n items: T[]\n /** Full filtered + sorted set, before pagination. */\n filtered: T[]\n /** Size of `filtered`. */\n total: number\n page: number\n pageCount: number\n pageSize: number\n isEmpty: boolean\n activeFilters: Record<string, string | undefined>\n setFilter: (key: string, value: string | undefined) => void\n clearFilters: () => void\n search: string\n setSearch: (term: string) => void\n sort: string | undefined\n setSort: (key: string | undefined) => void\n setPage: (page: number) => void\n nextPage: () => void\n prevPage: () => void\n}\n\nexport function useCollection<T>(options: UseCollectionOptions<T>): UseCollectionResult<T> {\n const { items, pageSize = 0, filters = [], search: searchFn, sorts = {} } = options\n\n const [activeFilters, setActiveFilters] = useState<Record<string, string | undefined>>(\n () => options.initialFilters ?? {},\n )\n const [search, setSearchState] = useState(() => options.initialSearch ?? '')\n const [sort, setSortState] = useState<string | undefined>(() => options.initialSort)\n const [page, setPageState] = useState(() => options.initialPage ?? 1)\n\n const filtered = useMemo(() => {\n let next = items\n\n for (const { key, predicate } of filters) {\n const value = activeFilters[key]\n if (value != null && value !== '') {\n next = next.filter((item) => predicate(item, value))\n }\n }\n\n const term = search.trim()\n if (searchFn && term) {\n next = next.filter((item) => searchFn(item, term))\n }\n\n const comparator = sort ? sorts[sort] : undefined\n if (comparator) {\n next = next.slice().sort(comparator)\n }\n\n return next\n }, [items, filters, activeFilters, search, searchFn, sort, sorts])\n\n const total = filtered.length\n const pageCount = pageSize > 0 ? Math.max(1, Math.ceil(total / pageSize)) : 1\n // Derive the effective page so a shrinking result set never strands us past the\n // last page — state stays as-is, but what we render is always clamped.\n const currentPage = Math.min(Math.max(1, page), pageCount)\n\n const pageItems = useMemo(() => {\n if (pageSize <= 0) return filtered\n const start = (currentPage - 1) * pageSize\n return filtered.slice(start, start + pageSize)\n }, [filtered, pageSize, currentPage])\n\n // Any change to what we're viewing snaps back to the first page.\n const resetToFirstPage = useCallback(() => setPageState(1), [])\n\n const setFilter = useCallback(\n (key: string, value: string | undefined) => {\n setActiveFilters((prev) => ({ ...prev, [key]: value }))\n resetToFirstPage()\n },\n [resetToFirstPage],\n )\n\n const clearFilters = useCallback(() => {\n setActiveFilters({})\n resetToFirstPage()\n }, [resetToFirstPage])\n\n const setSearch = useCallback(\n (term: string) => {\n setSearchState(term)\n resetToFirstPage()\n },\n [resetToFirstPage],\n )\n\n const setSort = useCallback(\n (key: string | undefined) => {\n setSortState(key)\n resetToFirstPage()\n },\n [resetToFirstPage],\n )\n\n // Navigation clamps against the live page bounds so a shrinking result set or\n // an over-shooting jump can never strand the state past the last page.\n const setPage = useCallback(\n (next: number) => setPageState(Math.min(Math.max(1, next), pageCount)),\n [pageCount],\n )\n const nextPage = useCallback(\n () => setPageState(Math.min(currentPage + 1, pageCount)),\n [currentPage, pageCount],\n )\n const prevPage = useCallback(() => setPageState(Math.max(1, currentPage - 1)), [currentPage])\n\n return {\n items: pageItems,\n filtered,\n total,\n page: currentPage,\n pageCount,\n pageSize,\n isEmpty: total === 0,\n activeFilters,\n setFilter,\n clearFilters,\n search,\n setSearch,\n sort,\n setSort,\n setPage,\n nextPage,\n prevPage,\n }\n}\n","'use client'\n\nimport * as React from 'react'\nimport { cn } from '@reblu/site-ui/utils'\n\nimport { useCollection, type CollectionFilter } from '../hooks/use-collection'\n\n/**\n * `Collection` — token-driven presentational grid over {@link useCollection}.\n *\n * The headless hook owns filtering/sorting/pagination; this component owns the\n * chrome: a facet chip bar, the item grid (via a `renderItem` prop), and simple\n * pagination. It carries no brand aesthetic — colors come from tokens; a facet\n * option may carry a `color` (tenant hex) which is applied through a CSS custom\n * property, never baked into a class name.\n *\n * Serves both the page index and the blog index; only the facets differ.\n */\nexport interface CollectionFacetOption {\n value: string\n label: string\n /** Optional tenant color (hex). Applied via a CSS var, not a hardcoded class. */\n color?: string\n}\n\nexport interface CollectionFacet {\n key: string\n label: string\n options: CollectionFacetOption[]\n /** Label for the \"clear this facet\" chip. Defaults to \"All\". */\n allLabel?: string\n}\n\nexport interface CollectionLabels {\n previous: string\n next: string\n page: (page: number, pageCount: number) => string\n}\n\nconst DEFAULT_LABELS: CollectionLabels = {\n previous: 'Previous',\n next: 'Next',\n page: (page, pageCount) => `Page ${page} of ${pageCount}`,\n}\n\nexport interface CollectionProps<T> {\n items: T[]\n renderItem: (item: T) => React.ReactNode\n getKey: (item: T) => string\n filters?: CollectionFilter<T>[]\n facets?: CollectionFacet[]\n pageSize?: number\n sorts?: Record<string, (a: T, b: T) => number>\n initialFilters?: Record<string, string | undefined>\n initialSort?: string\n emptyLabel?: string\n className?: string\n /** Tailwind classes for the grid container. */\n gridClassName?: string\n labels?: Partial<CollectionLabels>\n}\n\nexport function Collection<T>({\n items,\n renderItem,\n getKey,\n filters,\n facets = [],\n pageSize = 0,\n sorts,\n initialFilters,\n initialSort,\n emptyLabel = 'No results',\n className,\n gridClassName = 'grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3',\n labels,\n}: CollectionProps<T>) {\n const t = { ...DEFAULT_LABELS, ...labels }\n const collection = useCollection<T>({\n items,\n filters,\n pageSize,\n sorts,\n initialFilters,\n initialSort,\n })\n\n return (\n <div className={cn('space-y-8', className)}>\n {facets.length > 0 && (\n <div className='space-y-4'>\n {facets.map((facet) => {\n const active = collection.activeFilters[facet.key]\n return (\n <div\n key={facet.key}\n role='group'\n aria-label={facet.label}\n className='flex flex-wrap gap-2'\n >\n <FacetChip\n label={facet.allLabel ?? 'All'}\n pressed={active == null || active === ''}\n onClick={() => collection.setFilter(facet.key, undefined)}\n />\n {facet.options.map((opt) => (\n <FacetChip\n key={opt.value}\n label={opt.label}\n color={opt.color}\n pressed={active === opt.value}\n onClick={() => collection.setFilter(facet.key, opt.value)}\n />\n ))}\n </div>\n )\n })}\n </div>\n )}\n\n {collection.isEmpty ? (\n <p className='py-16 text-center text-muted-foreground'>{emptyLabel}</p>\n ) : (\n <ul className={cn('list-none p-0', gridClassName)}>\n {collection.items.map((item) => (\n <li key={getKey(item)}>{renderItem(item)}</li>\n ))}\n </ul>\n )}\n\n {collection.pageCount > 1 && (\n <nav aria-label='Pagination' className='flex items-center justify-center gap-4'>\n <button\n type='button'\n onClick={collection.prevPage}\n disabled={collection.page <= 1}\n className={PAGE_BTN_CLASS}\n >\n {t.previous}\n </button>\n <span aria-current='page' className='text-sm text-muted-foreground'>\n {t.page(collection.page, collection.pageCount)}\n </span>\n <button\n type='button'\n onClick={collection.nextPage}\n disabled={collection.page >= collection.pageCount}\n className={PAGE_BTN_CLASS}\n >\n {t.next}\n </button>\n </nav>\n )}\n </div>\n )\n}\n\nconst PAGE_BTN_CLASS =\n 'inline-flex h-9 cursor-pointer items-center rounded-md border border-border px-4 text-sm font-medium transition-colors hover:bg-muted disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'\n\ninterface FacetChipProps {\n label: string\n pressed: boolean\n color?: string\n onClick: () => void\n}\n\nfunction FacetChip({ label, pressed, color, onClick }: FacetChipProps) {\n return (\n <button\n type='button'\n aria-pressed={pressed}\n onClick={onClick}\n // The tenant color rides in a CSS var; the class picks it up when pressed,\n // so no hex string is ever baked into a Tailwind class.\n style={color ? ({ '--facet-color': color } as React.CSSProperties) : undefined}\n className={cn(\n 'inline-flex cursor-pointer items-center rounded-full border px-3 py-1 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n pressed\n ? color\n ? 'border-transparent bg-[var(--facet-color)] text-white'\n : 'border-transparent bg-primary text-primary-foreground'\n : 'border-border text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n {label}\n </button>\n )\n}\n","'use client'\n\nimport * as React from 'react'\nimport { cn } from '@reblu/site-ui/utils'\n\nimport { buildShareUrls } from '../lib/share-urls'\n\n/**\n * `ShareButtons` — social share row for a blog post (LinkedIn / X / Facebook /\n * WhatsApp) plus a copy-to-clipboard action. Token-driven and icon-only for the\n * providers; the copy action shows a text confirmation.\n *\n * Copy defaults to English (distributable default). A Spanish-or-other SITE can\n * override every visible string via `labels` without forking the component.\n */\nexport interface ShareButtonsLabels {\n share: string\n copy: string\n copied: string\n linkedin: string\n x: string\n facebook: string\n whatsapp: string\n copyStatus: string\n}\n\nconst DEFAULT_LABELS: ShareButtonsLabels = {\n share: 'Share',\n copy: 'Copy link',\n copied: 'Link copied',\n linkedin: 'Share on LinkedIn',\n x: 'Share on X',\n facebook: 'Share on Facebook',\n whatsapp: 'Share on WhatsApp',\n copyStatus: 'Link copied to clipboard',\n}\n\nexport interface ShareButtonsProps {\n /** Canonical, absolute URL of the post being shared. */\n url: string\n /** Post title, used where the provider supports pre-filled copy. */\n title: string\n className?: string\n labels?: Partial<ShareButtonsLabels>\n}\n\nconst LINK_CLASS =\n 'inline-flex h-9 w-9 items-center justify-center rounded-full border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'\n\nconst ICON = 'h-4 w-4'\n\nfunction LinkedInIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='currentColor' aria-hidden='true' focusable='false'>\n <path d='M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zM8.34 18.34V10.9H5.9v7.44zM7.12 9.82a1.42 1.42 0 1 0 0-2.84 1.42 1.42 0 0 0 0 2.84m11.22 8.52v-4.08c0-2.18-1.16-3.19-2.72-3.19a2.35 2.35 0 0 0-2.13 1.17v-1h-2.43v7.1h2.43v-3.93c0-1 .2-2 1.46-2s1.28 1.16 1.28 2.06v3.87z' />\n </svg>\n )\n}\n\nfunction XIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='currentColor' aria-hidden='true' focusable='false'>\n <path d='M18.244 2.25h3.308l-7.227 8.26 8.502 11.24h-6.66l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z' />\n </svg>\n )\n}\n\nfunction FacebookIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='currentColor' aria-hidden='true' focusable='false'>\n <path d='M22 12a10 10 0 1 0-11.56 9.88v-6.99H7.9V12h2.54V9.8c0-2.5 1.49-3.89 3.78-3.89 1.09 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56V12h2.78l-.44 2.89h-2.34v6.99A10 10 0 0 0 22 12' />\n </svg>\n )\n}\n\nfunction WhatsAppIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='currentColor' aria-hidden='true' focusable='false'>\n <path d='M12.04 2C6.58 2 2.13 6.45 2.13 11.91c0 1.75.46 3.45 1.32 4.95L2 22l5.25-1.38a9.9 9.9 0 0 0 4.79 1.22h.01c5.46 0 9.91-4.45 9.91-9.91 0-2.65-1.03-5.14-2.9-7.01A9.82 9.82 0 0 0 12.04 2m0 1.67c2.2 0 4.27.86 5.82 2.42a8.19 8.19 0 0 1 2.42 5.82c0 4.54-3.7 8.24-8.25 8.24a8.2 8.2 0 0 1-4.2-1.15l-.3-.18-3.12.82.83-3.04-.2-.31a8.2 8.2 0 0 1-1.26-4.38c0-4.54 3.7-8.24 8.25-8.24m4.52 10.36c-.06-.1-.22-.16-.46-.28s-1.46-.72-1.68-.8-.39-.12-.55.12-.63.8-.77.96-.28.18-.52.06a6.7 6.7 0 0 1-1.98-1.22 7.4 7.4 0 0 1-1.37-1.7c-.14-.24-.01-.37.11-.49.11-.11.24-.28.37-.42.12-.14.16-.24.24-.4s.04-.3-.02-.42-.55-1.32-.75-1.81c-.2-.48-.4-.41-.55-.42h-.47a.9.9 0 0 0-.65.3c-.22.24-.85.83-.85 2.02s.87 2.35 1 2.51c.12.16 1.71 2.61 4.14 3.66.58.25 1.03.4 1.38.51.58.19 1.11.16 1.53.1.47-.07 1.46-.6 1.66-1.17.21-.58.21-1.07.15-1.17' />\n </svg>\n )\n}\n\nfunction CopyIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='none' stroke='currentColor' strokeWidth='2' strokeLinecap='round' strokeLinejoin='round' aria-hidden='true' focusable='false'>\n <rect x='9' y='9' width='13' height='13' rx='2' ry='2' />\n <path d='M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1' />\n </svg>\n )\n}\n\nfunction CheckIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='none' stroke='currentColor' strokeWidth='2' strokeLinecap='round' strokeLinejoin='round' aria-hidden='true' focusable='false'>\n <polyline points='20 6 9 17 4 12' />\n </svg>\n )\n}\n\nexport function ShareButtons({ url, title, className, labels }: ShareButtonsProps) {\n const t = { ...DEFAULT_LABELS, ...labels }\n const [copied, setCopied] = React.useState(false)\n const share = buildShareUrls(url, title)\n\n async function handleCopy() {\n try {\n await navigator.clipboard.writeText(url)\n setCopied(true)\n window.setTimeout(() => setCopied(false), 2000)\n } catch {\n // Clipboard can be unavailable (permissions, insecure context); fail quietly.\n }\n }\n\n return (\n <div className={cn('flex items-center gap-2', className)} role='group' aria-label={t.share}>\n <span className='mr-1 text-sm font-medium text-muted-foreground'>{t.share}</span>\n\n <a href={share.linkedin} target='_blank' rel='noopener noreferrer' className={LINK_CLASS} aria-label={t.linkedin}>\n <LinkedInIcon />\n </a>\n <a href={share.x} target='_blank' rel='noopener noreferrer' className={LINK_CLASS} aria-label={t.x}>\n <XIcon />\n </a>\n <a href={share.facebook} target='_blank' rel='noopener noreferrer' className={LINK_CLASS} aria-label={t.facebook}>\n <FacebookIcon />\n </a>\n <a href={share.whatsapp} target='_blank' rel='noopener noreferrer' className={LINK_CLASS} aria-label={t.whatsapp}>\n <WhatsAppIcon />\n </a>\n\n <button\n type='button'\n onClick={handleCopy}\n className={cn(LINK_CLASS, 'w-auto cursor-pointer gap-2 px-3', copied && 'border-primary text-primary')}\n aria-label={t.copy}\n >\n {copied ? <CheckIcon /> : <CopyIcon />}\n <span className='text-sm'>{copied ? t.copied : t.copy}</span>\n </button>\n\n {/* Polite live region so screen readers announce the copy confirmation. */}\n <span role='status' aria-live='polite' className='sr-only'>\n {copied ? t.copyStatus : ''}\n </span>\n </div>\n )\n}\n","/**\n * Pure builders for the social-share targets used by `ShareButtons`.\n *\n * Framework-free so the URL construction is unit-testable without rendering.\n * Every provider gets the canonical post URL; providers that support custom copy\n * (X, WhatsApp) also receive the post title.\n */\nexport interface ShareUrls {\n linkedin: string\n x: string\n facebook: string\n whatsapp: string\n}\n\nexport function buildShareUrls(url: string, title: string): ShareUrls {\n const encodedUrl = encodeURIComponent(url)\n const encodedTitle = encodeURIComponent(title)\n\n return {\n // LinkedIn derives title/description from the target page's Open Graph tags,\n // so it only takes the URL.\n linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,\n // The intent endpoint keeps the /intent/tweet path under the x.com domain.\n x: `https://x.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`,\n facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,\n whatsapp: `https://wa.me/?text=${encodeURIComponent(`${title} ${url}`)}`,\n }\n}\n","'use client'\n\nimport { useEffect, useRef, useState } from 'react'\n\nimport { cn } from '@reblu/site-ui/utils'\n\n/**\n * `AddToCartButton` — add-to-cart CTA with a built-in confirmed/error state\n * (REB-837). Moved to the `./client` entry (from the RSC-safe root) because\n * the confirmed-state timer and the `error` prop watcher genuinely need\n * `useState`/`useEffect` — see `client.ts`'s own leading `'use client'`\n * directive (R4: tsup only preserves a directive declared by the entry file\n * itself, not one merely re-exported from a child module).\n *\n * Previously (REB-829) this component had no internal state at all — it was\n * a pure `onAddToCart?: () => void` button exported from the RSC-safe root.\n * A SITE importing `AddToCartButton` from `@reblu/site-blocks` (the root\n * entry) must now import it from `@reblu/site-blocks/client` instead.\n */\nexport interface AddToCartButtonLabels {\n add: string\n adding: string\n outOfStock: string\n /** Shown for ~`successDurationMs` after a successful add. */\n added: string\n /** Fallback shown when `error` is `true` (not a string). */\n error: string\n}\n\nconst DEFAULT_LABELS: AddToCartButtonLabels = {\n add: 'Añadir al carrito',\n adding: 'Añadiendo…',\n outOfStock: 'Agotado',\n added: 'Añadido ✓',\n error: 'No se pudo añadir',\n}\n\nexport type AddToCartButtonStatus = 'idle' | 'success' | 'error'\n\n/**\n * Structural subset of `@reblu/site-next/cart`'s `AddItemOutcome` (REB-837\n * review fix) — `site-blocks` never depends on `site-next`, so this is\n * duplicated shape, not an import; a real `AddItemOutcome` (which also\n * carries `quantity`) satisfies this type as-is.\n */\nexport interface AddToCartOutcome {\n status: 'added' | 'clamped' | 'refused'\n}\n\nexport interface AddToCartButtonProps<TItem = unknown> {\n /**\n * Called on click. MAY return the outcome of the add (synchronously or\n * via a `Promise`) so the button can decide success vs error from the\n * RESULT instead of only from the externally-controlled `error` prop\n * (REB-837 review fix: deciding purely from `error` captured at render\n * time showed \"Añadido ✓\" for a REFUSED add whenever the caller never\n * flips `error` back on). `'refused'` → error state; `'added'`/`'clamped'`\n * → confirmed state; returning nothing (`void`) stays backward compatible\n * with the original fire-and-forget contract — treated as success.\n */\n onAddToCart?: (item?: TItem) => AddToCartOutcome | void | Promise<AddToCartOutcome | void>\n disabled?: boolean\n outOfStock?: boolean\n /** True while the add-to-cart call (or a navigation it triggers) is in flight. */\n pending?: boolean\n /**\n * Controlled error (out-of-stock race, max-quantity reached, a failed\n * mutation…). Set it truthy right after `onAddToCart` fails to trigger the\n * error state; clear it (`undefined`/`false`) to let the button reset. A\n * `string` is shown as the button's label; `true` falls back to\n * `labels.error`.\n */\n error?: string | boolean\n /** Opaque item forwarded verbatim to `onAddToCart`/`onAdded`/`onError` — never read internally. */\n item?: TItem\n /** How long the success/error state shows before reverting to idle, ms. @default 1500 */\n successDurationMs?: number\n /** Fires once per successful add, right after `onAddToCart`. */\n onAdded?: (item?: TItem) => void\n /** Fires once when `error` transitions from falsy to truthy. */\n onError?: (item?: TItem, error?: string | boolean) => void\n labels?: Partial<AddToCartButtonLabels>\n className?: string\n}\n\nexport function AddToCartButton<TItem = unknown>({\n onAddToCart,\n disabled,\n outOfStock,\n pending,\n error,\n item,\n successDurationMs = 1500,\n onAdded,\n onError,\n labels,\n className,\n}: AddToCartButtonProps<TItem>) {\n const t = { ...DEFAULT_LABELS, ...labels }\n const [status, setStatus] = useState<AddToCartButtonStatus>('idle')\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const prevErrorRef = useRef<string | boolean | undefined>(error)\n // Non-reentrancy guard (REB-837): `disabled`/`aria-disabled` already block\n // both pointer and keyboard activation of a native <button>, but a ref\n // guard is the actual source of truth for \"is a success window open OR an\n // async onAddToCart is still resolving\" — defense in depth against\n // anything that might still dispatch a click, and the only thing that\n // actually blocks a double-submit while a Promise is pending.\n const inSuccessWindowRef = useRef(false)\n const resolvingRef = useRef(false)\n const mountedRef = useRef(true)\n // Mirrors `resolvingRef` into render state so the button is ALSO visibly\n // disabled (not just logically blocked) while an async `onAddToCart` is\n // still pending — `resolvingRef` alone is enough to stop a double-submit,\n // but a sighted user should see the button go inert too.\n const [resolving, setResolving] = useState(false)\n\n const clearTimer = () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n timeoutRef.current = null\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n return () => {\n mountedRef.current = false\n clearTimer()\n }\n }, [])\n\n const triggerSuccess = () => {\n inSuccessWindowRef.current = true\n clearTimer()\n setStatus('success')\n onAdded?.(item)\n timeoutRef.current = setTimeout(() => {\n inSuccessWindowRef.current = false\n setStatus('idle')\n }, successDurationMs)\n }\n\n const triggerError = (errorValue: string | boolean = true) => {\n clearTimer()\n inSuccessWindowRef.current = false\n setStatus('error')\n onError?.(item, errorValue)\n timeoutRef.current = setTimeout(() => setStatus('idle'), successDurationMs)\n }\n\n useEffect(() => {\n if (error && !prevErrorRef.current) {\n triggerError(error)\n }\n prevErrorRef.current = error\n // eslint-disable-next-line react-hooks/exhaustive-deps -- only re-run on `error` transitions\n }, [error])\n\n /**\n * Applies the RESULT of `onAddToCart` (REB-837 review fix): a `'refused'`\n * outcome always triggers the error state, regardless of the `error` prop\n * — the whole point is that the caller may not control that prop at all\n * (e.g. it derives the outcome from `addItem()`'s own return value, never\n * touching `error`). Anything else (`'added'`, `'clamped'`, or no return\n * value at all — the original fire-and-forget contract) is success,\n * UNLESS the externally-controlled `error` prop is ALSO currently truthy.\n */\n const applyOutcome = (result: AddToCartOutcome | void) => {\n if (!mountedRef.current || outOfStock) return\n if (result?.status === 'refused') {\n triggerError(true)\n return\n }\n if (error) return\n triggerSuccess()\n }\n\n const handleClick = () => {\n if (inSuccessWindowRef.current || resolvingRef.current) return\n const result = onAddToCart?.(item)\n\n if (result && typeof (result as Promise<AddToCartOutcome | void>).then === 'function') {\n resolvingRef.current = true\n setResolving(true)\n ;(result as Promise<AddToCartOutcome | void>)\n .then((resolved) => {\n resolvingRef.current = false\n if (mountedRef.current) setResolving(false)\n applyOutcome(resolved)\n })\n .catch(() => {\n resolvingRef.current = false\n if (mountedRef.current) setResolving(false)\n applyOutcome({ status: 'refused' })\n })\n return\n }\n\n applyOutcome(result as AddToCartOutcome | void)\n }\n\n const isDisabled = Boolean(disabled || outOfStock || pending || status === 'success' || resolving)\n const label = outOfStock\n ? t.outOfStock\n : status === 'success'\n ? t.added\n : status === 'error'\n ? typeof error === 'string'\n ? error\n : t.error\n : pending || resolving\n ? t.adding\n : t.add\n\n return (\n <button\n type='button'\n onClick={handleClick}\n disabled={isDisabled}\n aria-disabled={isDisabled}\n aria-live='polite'\n data-slot='add-to-cart-button'\n data-status={status}\n className={cn(\n 'inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n isDisabled\n ? 'cursor-not-allowed bg-muted text-muted-foreground'\n : 'bg-primary text-primary-foreground hover:bg-primary/90',\n status === 'error' && 'bg-destructive text-destructive-foreground',\n className,\n )}\n >\n {label}\n </button>\n )\n}\n","'use client'\n\nimport { useEffect, useRef, useState } from 'react'\n\nimport { cn } from '@reblu/site-ui/utils'\n\n/**\n * `CartIcon` — cart badge/link, with an opt-in count \"bump\" (REB-837). Moved\n * to the `./client` entry (from the RSC-safe root) because the bump\n * animation needs `useState`/`useEffect` to detect an itemCount increase —\n * see `client.ts`'s own leading `'use client'` directive (R4).\n *\n * The HYDRATION GOTCHA this guards against: `CartProvider` rehydrates\n * AFTER mount, so `itemCount` jumps `0 -> N` on every page load with a\n * non-empty persisted cart. Without a guard, that jump reads exactly like \"N\n * items were just added\" and bumps on every single page load. The fix:\n * `hydrated` (pass `useCart().hydrated` straight through) is compared\n * against its OWN previous value, not just itemCount's — a bump only fires\n * when the cart was ALREADY hydrated on the previous comparison AND\n * itemCount increased since. The simultaneous `false -> true` / `0 -> N`\n * rehydration transition fails that first condition; a real add afterwards\n * (hydrated already `true` on both sides) passes it.\n */\nexport interface CartIconLabels {\n cart: string\n itemsSuffix: (count: number) => string\n}\n\nconst DEFAULT_LABELS: CartIconLabels = {\n cart: 'Carrito',\n itemsSuffix: (count) => `${count} artículo${count === 1 ? '' : 's'}`,\n}\n\n/** How long the bump animation plays, ms. */\nconst BUMP_DURATION_MS = 400\n\nexport interface CartIconProps {\n itemCount: number\n href: string\n /**\n * Pass `useCart().hydrated` through when `bumpOnAdd` is enabled — gates\n * the bump against the rehydration jump. Omitting it ENTIRELY (REB-837\n * review fix — not the same as passing `false`) disables the hydration\n * guard altogether, so the bump fires on every itemCount increase; only\n * reach for that when `itemCount` is computed without any client-side\n * rehydration step (e.g. server-rendered, never changes after mount).\n */\n hydrated?: boolean\n /**\n * Enable the count bump when `itemCount` increases after hydration.\n * Respects `prefers-reduced-motion` (the animation classes are\n * `motion-safe:`-gated). @default false\n */\n bumpOnAdd?: boolean\n labels?: Partial<CartIconLabels>\n className?: string\n}\n\nexport function CartIcon({ itemCount, href, hydrated, bumpOnAdd, labels, className }: CartIconProps) {\n const t = { ...DEFAULT_LABELS, ...labels }\n const ariaLabel = itemCount > 0 ? `${t.cart} (${t.itemsSuffix(itemCount)})` : t.cart\n const displayCount = itemCount > 99 ? '99+' : String(itemCount)\n\n const [bumping, setBumping] = useState(false)\n const prevCountRef = useRef(itemCount)\n const prevHydratedRef = useRef(hydrated)\n\n useEffect(() => {\n // REB-837 review fix: `hydrated` OMITTED entirely (never `=== true` nor\n // `=== false`, i.e. `undefined` on the CURRENT render) means the caller\n // isn't using the hydration guard at all — treat it as always-satisfied\n // rather than permanently failing `prevHydratedRef.current === true`\n // (which stayed `undefined` forever and silently disabled every bump).\n const hydrationGuardSatisfied = hydrated === undefined || prevHydratedRef.current === true\n const shouldBump =\n Boolean(bumpOnAdd) && itemCount > prevCountRef.current && hydrationGuardSatisfied\n\n prevCountRef.current = itemCount\n prevHydratedRef.current = hydrated\n\n if (!shouldBump) {\n // REB-837 review fix: a PREVIOUS bump's own setTimeout gets cancelled\n // by this effect's cleanup (dep change) before this body runs — if\n // this run doesn't start a NEW bump, it must still reset the flag\n // itself, or a bump started just before a quick remove/decrease\n // (within `BUMP_DURATION_MS`) gets stuck on `true` forever.\n setBumping(false)\n return\n }\n\n setBumping(true)\n const id = setTimeout(() => setBumping(false), BUMP_DURATION_MS)\n return () => clearTimeout(id)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally only re-runs when itemCount/hydrated/bumpOnAdd change\n }, [itemCount, hydrated, bumpOnAdd])\n\n return (\n <a\n href={href}\n aria-label={ariaLabel}\n data-slot='cart-icon'\n data-bumping={bumping || undefined}\n className={cn(\n 'relative inline-flex items-center justify-center rounded-lg p-2 text-foreground/80 transition-colors hover:bg-muted/60 hover:text-foreground',\n className,\n )}\n >\n <svg\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth={2}\n strokeLinecap='round'\n strokeLinejoin='round'\n className={cn('h-5 w-5', bumping && 'motion-safe:scale-125 motion-safe:transition-transform motion-safe:duration-300')}\n aria-hidden='true'\n >\n <circle cx='9' cy='21' r='1' />\n <circle cx='20' cy='21' r='1' />\n <path d='M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6' />\n </svg>\n {itemCount > 0 && (\n <span\n aria-hidden='true'\n className={cn(\n 'absolute -top-0.5 -right-0.5 flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-primary px-1 text-[10px] font-bold leading-none tabular-nums text-primary-foreground',\n bumping && 'motion-safe:scale-125 motion-safe:transition-transform motion-safe:duration-300',\n )}\n >\n {displayCount}\n </span>\n )}\n </a>\n )\n}\n","'use client'\n\nimport { useCallback, useEffect, useId, useRef, useState, type ReactNode } from 'react'\n\nimport { buttonVariants } from '@reblu/site-ui/button-variants'\nimport { cn } from '@reblu/site-ui/utils'\nimport type { ShopProductListItem } from '@reblu/site-contracts'\n\nimport { Heading, type HeadingLevel } from './_heading'\nimport { ProductCard } from './product-card'\n\n/**\n * `ProductCarousel` — a horizontal product strip («Productos relacionados»,\n * «También te puede interesar»…) with no carousel dependency.\n *\n * - The track is a native scroll container with CSS scroll-snap, so touch users swipe\n * it and keyboard users scroll it once it has focus (it is a labelled, focusable region).\n * - Previous/next arrows (from `sm` up) scroll by one visible page. They are not rendered\n * when everything fits, and the arrow of the end the track is at is disabled.\n * - Arrow scrolling is instant under `prefers-reduced-motion: reduce`.\n * - Cards: the package's `ProductCard` by default, or the consumer's via `renderProduct`.\n */\nexport interface ProductCarouselLabels {\n /** Accessible name of the track when there is no `heading`. */\n region: string\n /** `aria-roledescription` of the track. */\n roleDescription: string\n previous: string\n next: string\n}\n\nexport const DEFAULT_PRODUCT_CAROUSEL_LABELS: ProductCarouselLabels = {\n region: 'Productos relacionados',\n roleDescription: 'carrusel',\n previous: 'Anterior',\n next: 'Siguiente',\n}\n\ninterface ProductCarouselCommonProps {\n /** Visible heading; when set it also names the track. */\n heading?: string\n /** Default: 2. */\n headingLevel?: HeadingLevel\n /** Replaces the heading's typography (see the className rule in the README). */\n headingClassName?: string\n labels?: Partial<ProductCarouselLabels>\n className?: string\n /** Classes for each slide (`<li>`), e.g. a different card width. */\n itemClassName?: string\n}\n\nexport interface ProductCarouselDefaultCardProps extends ProductCarouselCommonProps {\n products: readonly ShopProductListItem[]\n renderProduct?: undefined\n /** Link target per product (e.g. `(p) => \\`/shop/${p.slug}\\``). Omit for non-linking cards. */\n productHref?: (product: ShopProductListItem) => string | undefined\n currencyCode?: string\n locale?: string\n}\n\nexport interface ProductCarouselCustomCardProps<T extends { id: string | number }>\n extends ProductCarouselCommonProps {\n products: readonly T[]\n /** Renders one slide's card — your own product card. */\n renderProduct: (product: T, index: number) => ReactNode\n productHref?: undefined\n currencyCode?: undefined\n locale?: undefined\n}\n\nexport type ProductCarouselProps<T extends { id: string | number } = ShopProductListItem> =\n | ProductCarouselDefaultCardProps\n | ProductCarouselCustomCardProps<T>\n\n/** Tolerance for sub-pixel scroll positions. */\nconst EDGE_EPSILON = 1\n\nfunction prefersReducedMotion(): boolean {\n return typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n ? window.matchMedia('(prefers-reduced-motion: reduce)').matches\n : false\n}\n\nfunction ChevronIcon({ direction }: { direction: 'left' | 'right' }) {\n return (\n <svg\n aria-hidden='true'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth={2}\n strokeLinecap='round'\n strokeLinejoin='round'\n className='rtl:-scale-x-100'\n >\n <path d={direction === 'left' ? 'm15 18-6-6 6-6' : 'm9 18 6-6-6-6'} />\n </svg>\n )\n}\n\nexport function ProductCarousel<T extends { id: string | number } = ShopProductListItem>(\n props: ProductCarouselProps<T>,\n) {\n const { products, heading, headingLevel = 2, headingClassName, labels, className, itemClassName } = props\n const t = { ...DEFAULT_PRODUCT_CAROUSEL_LABELS, ...labels }\n\n const trackId = useId()\n const headingId = useId()\n const trackRef = useRef<HTMLDivElement>(null)\n const [canPrev, setCanPrev] = useState(false)\n const [canNext, setCanNext] = useState(false)\n\n const update = useCallback(() => {\n const el = trackRef.current\n if (!el) return\n const max = el.scrollWidth - el.clientWidth\n // RTL tracks report a negative scrollLeft; distance from the start is its magnitude.\n const pos = Math.abs(el.scrollLeft)\n setCanPrev(pos > EDGE_EPSILON)\n setCanNext(pos < max - EDGE_EPSILON)\n }, [])\n\n useEffect(() => {\n const el = trackRef.current\n if (!el) return\n update()\n el.addEventListener('scroll', update, { passive: true })\n let observer: ResizeObserver | undefined\n if (typeof ResizeObserver !== 'undefined') {\n observer = new ResizeObserver(update)\n observer.observe(el)\n } else {\n window.addEventListener('resize', update)\n }\n return () => {\n el.removeEventListener('scroll', update)\n if (observer) observer.disconnect()\n else window.removeEventListener('resize', update)\n }\n }, [update, products.length])\n\n const scrollByPage = (direction: -1 | 1) => {\n const el = trackRef.current\n if (!el) return\n const rtl = typeof getComputedStyle === 'function' && getComputedStyle(el).direction === 'rtl'\n el.scrollBy({\n left: direction * el.clientWidth * (rtl ? -1 : 1),\n behavior: prefersReducedMotion() ? 'auto' : 'smooth',\n })\n }\n\n if (products.length === 0) return null\n\n const scrollable = canPrev || canNext\n\n return (\n <section data-slot='product-carousel' className={cn('flex flex-col gap-4', className)}>\n {(heading || scrollable) && (\n <div className='flex items-center justify-between gap-4'>\n {heading ? (\n <div id={headingId}>\n <Heading level={headingLevel} className={headingClassName ?? 'text-xl font-semibold'}>\n {heading}\n </Heading>\n </div>\n ) : (\n <span />\n )}\n {scrollable && (\n <div className='hidden gap-2 sm:flex' data-slot='product-carousel-controls'>\n <button\n type='button'\n className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'rounded-full')}\n aria-label={t.previous}\n aria-controls={trackId}\n disabled={!canPrev}\n onClick={() => scrollByPage(-1)}\n >\n <ChevronIcon direction='left' />\n </button>\n <button\n type='button'\n className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'rounded-full')}\n aria-label={t.next}\n aria-controls={trackId}\n disabled={!canNext}\n onClick={() => scrollByPage(1)}\n >\n <ChevronIcon direction='right' />\n </button>\n </div>\n )}\n </div>\n )}\n\n <div\n ref={trackRef}\n id={trackId}\n role='region'\n aria-roledescription={t.roleDescription}\n {...(heading ? { 'aria-labelledby': headingId } : { 'aria-label': t.region })}\n tabIndex={0}\n data-slot='product-carousel-track'\n className='snap-x snap-mandatory overflow-x-auto overscroll-x-contain scroll-smooth rounded-lg pb-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:scroll-auto'\n >\n <ul role='list' className='m-0 flex list-none gap-4 p-0'>\n {products.map((product, index) => (\n <li\n key={product.id}\n className={cn(\n 'shrink-0 snap-start basis-[calc((100%-1rem)/1.5)] sm:basis-[calc((100%-2rem)/3)] lg:basis-[calc((100%-3rem)/4)]',\n itemClassName,\n )}\n >\n {props.renderProduct ? (\n props.renderProduct(product as T, index)\n ) : (\n <ProductCard\n product={product as ShopProductListItem}\n href={props.productHref?.(product as ShopProductListItem)}\n currencyCode={props.currencyCode}\n locale={props.locale}\n className='h-full'\n />\n )}\n </li>\n ))}\n </ul>\n </div>\n </section>\n )\n}\n","import type { ReactNode } from 'react'\n\n/** A heading level a caller can configure — `h1`…`h6`. */\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6\n\n/** One level deeper, clamped at `h6`. */\nexport function nextHeadingLevel(level: HeadingLevel): HeadingLevel {\n return Math.min(level + 1, 6) as HeadingLevel\n}\n\n/** Internal: renders `<h{level}>`. Server-safe (no hooks). */\nexport function Heading({\n level,\n className,\n children,\n}: {\n level: HeadingLevel\n className?: string\n children: ReactNode\n}) {\n const Tag = `h${level}` as const\n return <Tag className={className}>{children}</Tag>\n}\n","import { cn } from '@reblu/site-ui/utils'\nimport type { ShopProductListItem } from '@reblu/site-contracts'\n\nimport { formatPrice } from '../lib/format-price'\n\n/**\n * `ProductCard` — presentational shop product tile over `ShopProductListItem`.\n *\n * Token-driven, RSC-safe (no hooks). Prices arrive as integer cents and are\n * formatted with {@link formatPrice}. A `comparePrice` higher than `price` renders\n * as a struck-through \"was\" amount.\n */\nexport interface ProductCardProps {\n product: ShopProductListItem\n /** Link target for the product (e.g. `/shop/{slug}`). Omit to render non-linking. */\n href?: string\n currencyCode?: string\n locale?: string\n className?: string\n}\n\nexport function ProductCard({ product, href, currencyCode = 'EUR', locale, className }: ProductCardProps) {\n const image = product.images.find((i) => i.isPrimary) ?? product.images[0]\n const hasDiscount = product.comparePrice != null && product.comparePrice > product.price\n\n return (\n <article\n className={cn(\n 'group flex flex-col overflow-hidden rounded-lg border bg-card transition-shadow hover:shadow-lg',\n className,\n )}\n data-slot='product-card'\n >\n <div className='relative aspect-square overflow-hidden bg-muted'>\n {image ? (\n <img\n src={image.url}\n alt={image.alt ?? product.name}\n loading='lazy'\n className='h-full w-full object-cover transition-transform group-hover:scale-105'\n />\n ) : (\n <div className='flex h-full items-center justify-center text-4xl text-muted-foreground/30' aria-hidden='true'>\n 🛍️\n </div>\n )}\n </div>\n\n <div className='flex flex-1 flex-col gap-2 p-4'>\n {product.category && (\n <span className='inline-flex w-fit items-center rounded-full bg-secondary px-2.5 py-0.5 text-xs font-medium text-secondary-foreground'>\n {product.category.name}\n </span>\n )}\n\n <h3 className='text-base font-semibold leading-tight'>\n {href ? (\n <a href={href} className='transition-colors hover:text-primary'>\n {product.name}\n </a>\n ) : (\n product.name\n )}\n </h3>\n\n {product.shortDescription && (\n <p className='line-clamp-2 text-sm text-muted-foreground'>{product.shortDescription}</p>\n )}\n\n <div className='mt-auto flex items-baseline gap-2 pt-2'>\n <span className='text-lg font-bold text-foreground'>\n {formatPrice(product.price, currencyCode, locale)}\n </span>\n {hasDiscount && (\n <span className='text-sm text-muted-foreground line-through'>\n {formatPrice(product.comparePrice as number, currencyCode, locale)}\n </span>\n )}\n </div>\n </div>\n </article>\n )\n}\n","/**\n * Format an integer-cents amount as a localized currency string.\n *\n * All Reblu shop monetary values travel as integer cents (Prisma `Int`, never\n * Decimal), so the SITE never sees floats. This helper is the single place that\n * converts cents → major units and applies `Intl.NumberFormat`, keeping price\n * rendering consistent across `ProductCard` and any future shop organism.\n */\nexport function formatPrice(cents: number, currency = 'EUR', locale = 'es-ES'): string {\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n }).format(cents / 100)\n}\n","'use client'\n\nimport { useId, useState } from 'react'\n\nimport { cn } from '@reblu/site-ui/utils'\nimport type { AcademyLessonPublic, AcademyModulePublic } from '@reblu/site-contracts'\n\nimport type { HeadingLevel } from './_heading'\nimport { slotClassName } from '../lib/slot-class-name'\n\n/**\n * `CourseSyllabus` — the drop-in programme of a public course page, over\n * `AcademyCourseDetail.modules` (`client.academy.bySlug()`), REB-1134; redesigned\n * in REB-1135.\n *\n * - The header reads the heading, then «N módulos · M lecciones · total», and\n * carries an «Expandir todo / Contraer todo» control.\n * - Each module with lessons is a `<button>` with `aria-expanded` and\n * `aria-controls`, showing its lesson count and, when every lesson has one,\n * the module's total duration, with a chevron that rotates when open.\n * - Each lesson row shows a type icon (video / reading, named for screen\n * readers), its title, its duration as «m:ss» / «h:mm:ss», and — on a\n * free-preview lesson — «Vista previa gratuita» as a call to action:\n * a link (`getPreviewHref` / `previewHrefTemplate`), a button (`onPreview`),\n * or a plain label when the SITE wires neither.\n * - A module without lessons is a plain row (title only), never a button.\n *\n * It renders only what the API serves: published lessons, no video, playback\n * or attachment data. The preview itself is fetched by the page it links to\n * (`getLessonPreview` in `@reblu/site-next/academy-server`).\n *\n * Client Component: it holds the open/closed state. Copy and\n * `previewHrefTemplate` are serialisable, so a Server Component page can render\n * it directly; `getPreviewHref` / `onPreview` are functions, so they need a\n * Client Component parent.\n */\nexport interface CourseSyllabusLabels {\n heading: string\n moduleSingular: string\n modulePlural: string\n lessonSingular: string\n lessonPlural: string\n /** Call to action (or label) on a lesson with `isFreePreview`. */\n freePreview: string\n /** Hours unit in module/course totals («1 h 15 min»). */\n hours: string\n /** Minutes unit in module/course totals. */\n minutes: string\n expandAll: string\n collapseAll: string\n /** Screen-reader name of the video lesson icon. */\n videoLesson: string\n /** Screen-reader name of the text (reading) lesson icon. */\n textLesson: string\n}\n\nexport const DEFAULT_COURSE_SYLLABUS_LABELS: CourseSyllabusLabels = {\n heading: 'Programa',\n moduleSingular: 'módulo',\n modulePlural: 'módulos',\n lessonSingular: 'lección',\n lessonPlural: 'lecciones',\n freePreview: 'Vista previa gratuita',\n hours: 'h',\n minutes: 'min',\n expandAll: 'Expandir todo',\n collapseAll: 'Contraer todo',\n videoLesson: 'Vídeo',\n textLesson: 'Lectura',\n}\n\n/** A module as served. `lessons` is optional so an older payload still renders. */\nexport type CourseSyllabusModule = Pick<AcademyModulePublic, 'id' | 'title'> & {\n description?: string | null\n lessons?: readonly AcademyLessonPublic[]\n}\n\nexport interface CourseSyllabusProps {\n /** `AcademyCourseDetail.modules`, in the order the API returns them. */\n modules: readonly CourseSyllabusModule[]\n labels?: Partial<CourseSyllabusLabels>\n /** Level of the heading. Default `2`. */\n headingLevel?: HeadingLevel\n /** Replaces the heading's default typography (see the className rule in the README). */\n headingClassName?: string\n className?: string\n /**\n * Link target of a free-preview lesson, e.g. `/cursos/${slug}/preview/${l.slug}`.\n * Called only for lessons with `isFreePreview`. Return `undefined` to keep the label.\n */\n getPreviewHref?: (lesson: AcademyLessonPublic) => string | undefined\n /**\n * Serialisable alternative to `getPreviewHref` for Server Component pages:\n * `{lessonSlug}` and `{lessonId}` are replaced (URL-encoded). Ignored when\n * `getPreviewHref` is given.\n */\n previewHrefTemplate?: string\n /** Renders the call to action as a `<button>` (e.g. to open a modal). Used when no href resolves. */\n onPreview?: (lesson: AcademyLessonPublic) => void\n}\n\nconst SEPARATOR = ' · '\n\nfunction count(n: number, singular: string, plural: string): string {\n return `${n} ${n === 1 ? singular : plural}`\n}\n\n/** Module / course total: «15 min», «1 h», «1 h 15 min». */\nfunction formatTotal(seconds: number, labels: CourseSyllabusLabels): string {\n const totalMinutes = Math.max(1, Math.round(seconds / 60))\n const hours = Math.floor(totalMinutes / 60)\n const minutes = totalMinutes % 60\n if (hours === 0) return `${minutes} ${labels.minutes}`\n return minutes === 0\n ? `${hours} ${labels.hours}`\n : `${hours} ${labels.hours} ${minutes} ${labels.minutes}`\n}\n\nfunction splitSeconds(seconds: number): [number, number, number] {\n const whole = Math.round(seconds)\n return [Math.floor(whole / 3600), Math.floor((whole % 3600) / 60), whole % 60]\n}\n\n/** Lesson length: «m:ss», or «h:mm:ss» from one hour. */\nfunction formatClock(seconds: number): string {\n const [h, m, s] = splitSeconds(seconds)\n const ss = String(s).padStart(2, '0')\n return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`\n}\n\n/** ISO 8601 duration for `<time datetime>`: `PT10M0S`, `PT1H15M0S`. */\nfunction isoDuration(seconds: number): string {\n const [h, m, s] = splitSeconds(seconds)\n return `PT${h > 0 ? `${h}H` : ''}${m}M${s}S`\n}\n\nfunction hasDuration(lesson: AcademyLessonPublic): lesson is AcademyLessonPublic & {\n durationSeconds: number\n} {\n return typeof lesson.durationSeconds === 'number' && lesson.durationSeconds > 0\n}\n\n/** Sum of the lessons' durations, or `null` unless every lesson has one. */\nfunction totalSeconds(lessons: readonly AcademyLessonPublic[]): number | null {\n if (lessons.length === 0) return null\n let total = 0\n for (const l of lessons) {\n if (!hasDuration(l)) return null\n total += l.durationSeconds\n }\n return total\n}\n\nfunction fillTemplate(template: string, lesson: AcademyLessonPublic): string {\n return template\n .replaceAll('{lessonSlug}', encodeURIComponent(lesson.slug))\n .replaceAll('{lessonId}', encodeURIComponent(lesson.id))\n}\n\nconst ICON_PROPS = {\n 'aria-hidden': true,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n} as const\n\nfunction LessonIcon({ type }: { type: 'video' | 'text' }) {\n return (\n <svg\n {...ICON_PROPS}\n data-slot='course-syllabus-lesson-icon'\n data-type={type}\n className='size-4 shrink-0 text-muted-foreground'\n >\n {type === 'video' ? (\n <>\n <circle cx='12' cy='12' r='9' />\n <path d='m10 8.5 5 3.5-5 3.5z' />\n </>\n ) : (\n <>\n <path d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z' />\n <path d='M14 3v5h5M9 13h6M9 17h4' />\n </>\n )}\n </svg>\n )\n}\n\nfunction PlayIcon() {\n return (\n <svg {...ICON_PROPS} className='size-3.5 shrink-0'>\n <path d='m7 4 13 8-13 8z' fill='currentColor' />\n </svg>\n )\n}\n\nconst PREVIEW_CTA =\n 'inline-flex items-center gap-1.5 rounded-full border border-primary/40 bg-primary/10 px-3 py-1 text-xs font-semibold text-primary transition-colors hover:bg-primary hover:text-primary-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none'\n\nexport function CourseSyllabus({\n modules,\n labels: labelOverrides,\n headingLevel = 2,\n headingClassName,\n className,\n getPreviewHref,\n previewHrefTemplate,\n onPreview,\n}: CourseSyllabusProps) {\n const labels = { ...DEFAULT_COURSE_SYLLABUS_LABELS, ...labelOverrides }\n const baseId = useId()\n const [open, setOpen] = useState<ReadonlySet<string>>(() => new Set())\n\n if (modules.length === 0) return null\n\n const expandable = modules.filter((m) => (m.lessons?.length ?? 0) > 0).map((m) => m.id)\n const allLessons = modules.flatMap((m) => m.lessons ?? [])\n const courseSeconds = totalSeconds(allLessons)\n const summary = [count(modules.length, labels.moduleSingular, labels.modulePlural)]\n if (allLessons.length > 0)\n summary.push(count(allLessons.length, labels.lessonSingular, labels.lessonPlural))\n if (courseSeconds !== null) summary.push(formatTotal(courseSeconds, labels))\n\n const allOpen = expandable.length > 0 && expandable.every((id) => open.has(id))\n const headingId = `${baseId}-heading`\n const Tag = `h${headingLevel}` as const\n\n function toggle(id: string) {\n setOpen((prev) => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n function previewHref(lesson: AcademyLessonPublic): string | undefined {\n if (getPreviewHref) return getPreviewHref(lesson)\n return previewHrefTemplate ? fillTemplate(previewHrefTemplate, lesson) : undefined\n }\n\n function renderPreview(lesson: AcademyLessonPublic) {\n const name = `${labels.freePreview}: ${lesson.title}`\n const href = previewHref(lesson)\n if (href) {\n return (\n <a href={href} aria-label={name} data-slot='course-syllabus-preview' className={PREVIEW_CTA}>\n <PlayIcon />\n {labels.freePreview}\n </a>\n )\n }\n if (onPreview) {\n return (\n <button\n type='button'\n aria-label={name}\n data-slot='course-syllabus-preview'\n onClick={() => onPreview(lesson)}\n className={cn(PREVIEW_CTA, 'cursor-pointer')}\n >\n <PlayIcon />\n {labels.freePreview}\n </button>\n )\n }\n return (\n <span\n data-slot='course-syllabus-preview'\n className='rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-muted-foreground'\n >\n {labels.freePreview}\n </span>\n )\n }\n\n return (\n <section\n data-slot='course-syllabus'\n aria-labelledby={headingId}\n className={cn('space-y-4', className)}\n >\n <div\n data-slot='course-syllabus-header'\n className='flex flex-wrap items-end justify-between gap-x-4 gap-y-2'\n >\n <div className='space-y-1'>\n <Tag\n id={headingId}\n className={slotClassName('', 'text-xl font-semibold', headingClassName)}\n >\n {labels.heading}\n </Tag>\n <p data-slot='course-syllabus-summary' className='text-sm text-muted-foreground'>\n {summary.join(SEPARATOR)}\n </p>\n </div>\n {expandable.length > 0 && (\n <button\n type='button'\n data-slot='course-syllabus-toggle-all'\n onClick={() => setOpen(allOpen ? new Set() : new Set(expandable))}\n className='cursor-pointer rounded-md px-2 py-1 text-sm font-semibold text-primary underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'\n >\n {allOpen ? labels.collapseAll : labels.expandAll}\n </button>\n )}\n </div>\n\n <ol className='list-none divide-y divide-border overflow-hidden rounded-xl border border-border bg-card p-0'>\n {modules.map((module, index) => {\n const lessons = module.lessons ?? []\n const number = (\n <span\n aria-hidden='true'\n className='flex size-8 shrink-0 items-center justify-center rounded-full bg-primary/10 text-sm font-semibold text-primary tabular-nums'\n >\n {index + 1}\n </span>\n )\n\n if (lessons.length === 0) {\n return (\n <li\n key={module.id}\n data-slot='course-syllabus-module'\n className='flex items-center gap-4 px-4 py-4 sm:px-5'\n >\n {number}\n <span className='font-semibold leading-snug'>{module.title}</span>\n </li>\n )\n }\n\n const isOpen = open.has(module.id)\n const panelId = `${baseId}-module-${index}`\n const seconds = totalSeconds(lessons)\n const meta = [count(lessons.length, labels.lessonSingular, labels.lessonPlural)]\n if (seconds !== null) meta.push(formatTotal(seconds, labels))\n\n return (\n <li key={module.id} data-slot='course-syllabus-module'>\n <button\n type='button'\n aria-expanded={isOpen}\n aria-controls={panelId}\n onClick={() => toggle(module.id)}\n className={cn(\n 'flex w-full cursor-pointer items-center gap-4 px-4 py-4 text-left transition-colors hover:bg-muted/60 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none sm:px-5',\n isOpen && 'bg-muted/40',\n )}\n >\n {number}\n <span className='flex min-w-0 flex-1 flex-col gap-0.5'>\n <span className='font-semibold leading-snug'>{module.title}</span>\n <span className='text-sm text-muted-foreground'>{meta.join(SEPARATOR)}</span>\n </span>\n <svg\n {...ICON_PROPS}\n data-slot='course-syllabus-chevron'\n className={cn(\n 'size-5 shrink-0 text-muted-foreground transition-transform duration-200 motion-reduce:transition-none',\n isOpen && 'rotate-180',\n )}\n >\n <path d='m6 9 6 6 6-6' />\n </svg>\n </button>\n <ol\n id={panelId}\n hidden={!isOpen}\n data-slot='course-syllabus-lessons'\n className='list-none border-t border-border p-0 py-1'\n >\n {lessons.map((lesson) => {\n const type = lesson.hasVideo ? 'video' : 'text'\n return (\n <li\n key={lesson.id}\n data-slot='course-syllabus-lesson'\n className='flex flex-wrap items-center gap-x-3 gap-y-1.5 px-4 py-2.5 text-sm sm:pl-[4.25rem] sm:pr-5'\n >\n <LessonIcon type={type} />\n <span className='sr-only'>\n {type === 'video' ? labels.videoLesson : labels.textLesson}\n </span>\n <span\n data-slot='course-syllabus-lesson-title'\n className='min-w-0 flex-1 leading-snug'\n >\n {lesson.title}\n </span>\n {lesson.isFreePreview && renderPreview(lesson)}\n {hasDuration(lesson) && (\n <time\n data-slot='course-syllabus-lesson-duration'\n dateTime={isoDuration(lesson.durationSeconds)}\n className='ml-auto shrink-0 text-muted-foreground tabular-nums'\n >\n {formatClock(lesson.durationSeconds)}\n </time>\n )}\n </li>\n )\n })}\n </ol>\n </li>\n )\n })}\n </ol>\n </section>\n )\n}\n","import { cn } from '@reblu/site-ui/utils'\n\n/**\n * `text-*` utilities tailwind-merge classifies correctly (alignment, wrapping,\n * overflow). Every other `text-*` may be a SITE theme token it misreads.\n */\nconst MERGE_SAFE_TEXT = /(?:^|:)!?text-(?:left|center|right|justify|start|end|wrap|nowrap|balance|pretty|ellipsis|clip)$/\nconst TEXT_UTILITY = /(?:^|:)!?-?text-/\n\nfunction bypassesMerge(token: string): boolean {\n return TEXT_UTILITY.test(token) && !MERGE_SAFE_TEXT.test(token)\n}\n\n/**\n * Classes for a drop-in element a consumer can style (REB-997).\n *\n * tailwind-merge only knows Tailwind's default scale: a SITE's custom font-size\n * token such as `text-h2` is read as a text COLOUR and silently dropped against\n * a real colour (`twMerge('text-h2 text-ink')` → `text-ink`). So:\n *\n * - no consumer class (`undefined` or blank) → `cn(structural, defaults)`,\n * exactly what the component rendered before;\n * - with a consumer class → the stylistic `defaults` are NOT applied (the\n * consumer owns the look of that slot); `structural` — the classes the\n * element needs to work — is kept and merged with the consumer's layout\n * classes, while the consumer's `text-*` classes skip tailwind-merge and are\n * appended verbatim, so custom tokens reach the DOM intact.\n */\nexport function slotClassName(structural: string, defaults: string, consumer: string | undefined): string {\n if (consumer === undefined || consumer.trim() === '') return cn(structural, defaults)\n const tokens = consumer.split(/\\s+/).filter(Boolean)\n const verbatim = tokens.filter(bypassesMerge)\n const merged = cn(structural, tokens.filter((token) => !bypassesMerge(token)))\n return [merged, ...verbatim].filter(Boolean).join(' ')\n}\n","import { useEffect, useId, useRef, useState, type FormEvent, type ReactNode } from 'react'\nimport { cn } from '@reblu/site-ui/utils'\nimport {\n DATA_REQUEST_TYPES,\n type DataRequestInput,\n type DataRequestSubmission,\n type DataRequestType,\n} from '@reblu/site-contracts'\n\n/**\n * `DataRequestForm` — drop-in public GDPR rights form (REB-980) over\n * `POST /api/site/data-requests` (REB-77). The visitor picks the right they\n * exercise, identifies themselves, accepts the processing notice and submits;\n * the request lands in the tenant's nexus queue with source `PUBLIC_FORM` and\n * its 72h clock running, and the form shows the response deadline.\n *\n * Stateful (client-only — ships from `./client`) but transport-agnostic:\n * - `getCaptchaToken` mints the reCAPTCHA v3 token. base-api rejects a\n * request without one, so the form refuses to submit when it resolves\n * `undefined`. Pair it with `useDeferredRecaptchaToken` from\n * `@reblu/site-next/recaptcha-provider`.\n * - `submit` sends the body. By default it POSTs JSON to the SITE's own\n * Route Handler (`endpoint`, default `/api/data-requests`) and unwraps the\n * `{ data }` envelope — the API key never reaches the browser.\n *\n * Errors never show server text: a rejection is mapped by HTTP status to the\n * fixed (overridable) copy in `labels`, so nothing upstream — validation\n * details, provider state — reaches the visitor.\n */\nexport type DataRequestSubmitResult =\n | { ok: true; data: DataRequestSubmission }\n /** `status` is the HTTP status, or `0` when the request never got an answer. */\n | { ok: false; status: number }\n\n/**\n * Every GDPR right, in base-api's order. Lives in `@reblu/site-contracts`\n * since REB-1005 (server code needs it too); re-exported here so existing\n * `@reblu/site-blocks/client` imports keep working.\n */\nexport { DATA_REQUEST_TYPES }\n\n/** The server's field limits (base-api `POST /api/site/data-requests`). */\nexport const DATA_REQUEST_FIELD_LIMITS = Object.freeze({ name: 200, email: 320, details: 5000 })\n\nexport interface DataRequestFormLabels {\n requestTypeLabel: string\n requestTypePlaceholder: string\n requestTypes: Record<DataRequestType, string>\n nameLabel: string\n emailLabel: string\n detailsLabel: string\n detailsPlaceholder: string\n consent: ReactNode\n submit: string\n submitting: string\n requestTypeRequired: string\n nameRequired: string\n emailInvalid: string\n consentRequired: string\n /** Shown on success; receives the formatted 72h deadline. */\n success: (deadline: string) => ReactNode\n captchaError: string\n validationError: string\n rateLimitError: string\n genericError: string\n}\n\nexport const DEFAULT_DATA_REQUEST_FORM_LABELS: DataRequestFormLabels = {\n requestTypeLabel: 'Derecho que quieres ejercer',\n requestTypePlaceholder: 'Selecciona una opción',\n requestTypes: {\n ACCESS: 'Acceso',\n RECTIFICATION: 'Rectificación',\n ERASURE: 'Supresión',\n OBJECTION: 'Oposición',\n PORTABILITY: 'Portabilidad',\n RESTRICTION: 'Limitación del tratamiento',\n AUTOMATED_DECISION: 'No ser objeto de decisiones automatizadas',\n INFORMATION: 'Información',\n },\n nameLabel: 'Nombre y apellidos',\n emailLabel: 'Email',\n detailsLabel: 'Mensaje (opcional)',\n detailsPlaceholder: 'Describe tu solicitud',\n consent: 'Acepto que mis datos se traten para gestionar esta solicitud.',\n submit: 'Enviar solicitud',\n submitting: 'Enviando…',\n requestTypeRequired: 'Elige el derecho que quieres ejercer.',\n nameRequired: 'Indica tu nombre.',\n emailInvalid: 'Introduce un email válido.',\n consentRequired: 'Debes aceptar el tratamiento de tus datos para gestionar la solicitud.',\n success: (deadline) => (\n <>\n Hemos recibido tu solicitud. Te responderemos como máximo el {deadline}.\n </>\n ),\n captchaError:\n 'No hemos podido verificar que no eres un robot. Recarga la página e inténtalo de nuevo.',\n validationError: 'Revisa los datos del formulario e inténtalo de nuevo.',\n rateLimitError: 'Has enviado demasiadas solicitudes. Espera unos minutos e inténtalo de nuevo.',\n genericError: 'No hemos podido enviar tu solicitud. Inténtalo de nuevo más tarde.',\n}\n\nexport interface DataRequestFormProps {\n /** Mints the reCAPTCHA v3 token; `undefined` blocks the submission. */\n getCaptchaToken: () => Promise<string | undefined>\n /** Sends the body. Defaults to a JSON POST to `endpoint`. */\n submit?: (input: DataRequestInput) => Promise<DataRequestSubmitResult>\n /** The SITE's Route Handler used by the default `submit`. @default '/api/data-requests' */\n endpoint?: string\n /** First interaction with the form — wire it to the deferred reCAPTCHA `arm`. */\n onInteract?: () => void\n /** Called with the acknowledgement after a successful submission. */\n onSubmitted?: (ack: DataRequestSubmission) => void\n /** Rights offered, in order. @default DATA_REQUEST_TYPES */\n requestTypes?: readonly DataRequestType[]\n labels?: Partial<DataRequestFormLabels>\n /** Locale for the deadline. @default 'es-ES' */\n locale?: string\n className?: string\n}\n\ntype FieldName = 'requestType' | 'name' | 'email' | 'consent'\ntype FieldErrors = Partial<Record<FieldName, string>>\n\n/** Form order — focus goes to the first invalid field in this order. */\nconst FIELD_ORDER: readonly FieldName[] = ['requestType', 'name', 'email', 'consent']\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nasync function postToRoute(endpoint: string, input: DataRequestInput): Promise<DataRequestSubmitResult> {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) return { ok: false, status: res.status }\n const json = (await res.json()) as { data?: DataRequestSubmission }\n return json.data ? { ok: true, data: json.data } : { ok: false, status: res.status }\n } catch {\n return { ok: false, status: 0 }\n }\n}\n\nfunction rejectionMessage(status: number, t: DataRequestFormLabels): string {\n if (status === 400) return t.validationError\n if (status === 422) return t.captchaError\n if (status === 429) return t.rateLimitError\n return t.genericError\n}\n\nconst FIELD_CLASS =\n 'w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20'\n\nexport function DataRequestForm({\n getCaptchaToken,\n submit,\n endpoint = '/api/data-requests',\n onInteract,\n onSubmitted,\n requestTypes = DATA_REQUEST_TYPES,\n labels,\n locale = 'es-ES',\n className,\n}: DataRequestFormProps) {\n const t: DataRequestFormLabels = {\n ...DEFAULT_DATA_REQUEST_FORM_LABELS,\n ...labels,\n requestTypes: { ...DEFAULT_DATA_REQUEST_FORM_LABELS.requestTypes, ...labels?.requestTypes },\n }\n const id = useId()\n const [requestType, setRequestType] = useState<DataRequestType | ''>('')\n const [name, setName] = useState('')\n const [email, setEmail] = useState('')\n const [details, setDetails] = useState('')\n const [consent, setConsent] = useState(false)\n const [errors, setErrors] = useState<FieldErrors>({})\n const [formError, setFormError] = useState<string | null>(null)\n const [submitting, setSubmitting] = useState(false)\n const [ack, setAck] = useState<DataRequestSubmission | null>(null)\n const statusRef = useRef<HTMLDivElement>(null)\n\n // REB-1003: the success message replaces the form, so focus would otherwise\n // fall back to <body> — move it to the status the screen reader announces.\n useEffect(() => {\n if (ack) statusRef.current?.focus()\n }, [ack])\n\n async function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault()\n if (submitting) return\n setFormError(null)\n\n const next: FieldErrors = {}\n if (!requestType) next.requestType = t.requestTypeRequired\n if (!name.trim()) next.name = t.nameRequired\n if (!EMAIL_PATTERN.test(email.trim())) next.email = t.emailInvalid\n if (!consent) next.consent = t.consentRequired\n setErrors(next)\n const firstInvalid = FIELD_ORDER.find((field) => next[field])\n if (firstInvalid) {\n // REB-1003: take the visitor to the first field to fix.\n document.getElementById(`${id}-${firstInvalid === 'requestType' ? 'type' : firstInvalid}`)?.focus()\n return\n }\n if (!requestType) return\n\n setSubmitting(true)\n try {\n const captcha = await getCaptchaToken().catch(() => undefined)\n if (!captcha) {\n setFormError(t.captchaError)\n return\n }\n const input: DataRequestInput = {\n requestType,\n name: name.trim(),\n email: email.trim(),\n ...(details.trim() ? { details: details.trim() } : {}),\n captcha,\n }\n const result = await (submit ?? ((body) => postToRoute(endpoint, body)))(input).catch(\n (): DataRequestSubmitResult => ({ ok: false, status: 0 }),\n )\n if (!result.ok) {\n setFormError(rejectionMessage(result.status, t))\n return\n }\n setAck(result.data)\n onSubmitted?.(result.data)\n } finally {\n setSubmitting(false)\n }\n }\n\n if (ack) {\n const deadline = new Intl.DateTimeFormat(locale, {\n dateStyle: 'long',\n timeStyle: 'short',\n }).format(new Date(ack.slaDueAt))\n return (\n <div\n ref={statusRef}\n tabIndex={-1}\n role='status'\n data-slot='data-request-form-success'\n className={cn('rounded-lg border border-border p-6 text-sm', className)}>\n {t.success(deadline)}\n </div>\n )\n }\n\n const errorId = (field: keyof FieldErrors) => (errors[field] ? `${id}-${field}-error` : undefined)\n const fieldError = (field: keyof FieldErrors) =>\n errors[field] ? (\n <p id={`${id}-${field}-error`} className='text-xs text-destructive'>\n {errors[field]}\n </p>\n ) : null\n\n return (\n <form\n noValidate\n onSubmit={handleSubmit}\n onFocusCapture={onInteract}\n onPointerDown={onInteract}\n data-slot='data-request-form'\n className={cn('space-y-4', className)}\n >\n <div className='space-y-1'>\n <label htmlFor={`${id}-type`} className='text-sm font-medium'>\n {t.requestTypeLabel}\n </label>\n <select\n id={`${id}-type`}\n value={requestType}\n onChange={(e) => setRequestType(e.target.value as DataRequestType | '')}\n aria-invalid={errors.requestType ? true : undefined}\n aria-describedby={errorId('requestType')}\n className={cn(FIELD_CLASS, errors.requestType ? 'border-destructive' : 'border-border')}\n >\n <option value=''>{t.requestTypePlaceholder}</option>\n {requestTypes.map((type) => (\n <option key={type} value={type}>\n {t.requestTypes[type]}\n </option>\n ))}\n </select>\n {fieldError('requestType')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-name`} className='text-sm font-medium'>\n {t.nameLabel}\n </label>\n <input\n id={`${id}-name`}\n type='text'\n autoComplete='name'\n maxLength={DATA_REQUEST_FIELD_LIMITS.name}\n value={name}\n onChange={(e) => setName(e.target.value)}\n aria-invalid={errors.name ? true : undefined}\n aria-describedby={errorId('name')}\n className={cn(FIELD_CLASS, errors.name ? 'border-destructive' : 'border-border')}\n />\n {fieldError('name')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-email`} className='text-sm font-medium'>\n {t.emailLabel}\n </label>\n <input\n id={`${id}-email`}\n type='email'\n autoComplete='email'\n maxLength={DATA_REQUEST_FIELD_LIMITS.email}\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n aria-invalid={errors.email ? true : undefined}\n aria-describedby={errorId('email')}\n className={cn(FIELD_CLASS, errors.email ? 'border-destructive' : 'border-border')}\n />\n {fieldError('email')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-details`} className='text-sm font-medium'>\n {t.detailsLabel}\n </label>\n <textarea\n id={`${id}-details`}\n rows={4}\n maxLength={DATA_REQUEST_FIELD_LIMITS.details}\n value={details}\n onChange={(e) => setDetails(e.target.value)}\n placeholder={t.detailsPlaceholder}\n className={cn(FIELD_CLASS, 'border-border')}\n />\n </div>\n\n <div className='space-y-1'>\n <div className='flex items-start gap-2'>\n <input\n id={`${id}-consent`}\n type='checkbox'\n checked={consent}\n onChange={(e) => setConsent(e.target.checked)}\n aria-invalid={errors.consent ? true : undefined}\n aria-describedby={errorId('consent')}\n className='mt-1'\n />\n <label htmlFor={`${id}-consent`} className='text-sm text-foreground/80'>\n {t.consent}\n </label>\n </div>\n {fieldError('consent')}\n </div>\n\n {formError && (\n <p role='alert' className='text-sm text-destructive'>\n {formError}\n </p>\n )}\n\n <button\n type='submit'\n disabled={submitting}\n className='cursor-pointer rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:cursor-not-allowed disabled:opacity-60'\n >\n {submitting ? t.submitting : t.submit}\n </button>\n </form>\n )\n}\n","import { useEffect, useId, useRef, useState, type FormEvent, type ReactNode } from 'react'\nimport { cn } from '@reblu/site-ui/utils'\nimport type { WithdrawalRequestInput, WithdrawalRequestSubmission } from '@reblu/site-contracts'\n\n/**\n * `WithdrawalRequestForm` — drop-in withdrawal form (REB-1201) over\n * `POST /api/site/withdrawal-requests`. The buyer gives the order reference,\n * their email, what they withdraw from and, optionally, a reason; base-api\n * stores it, emails the tenant team and sends the buyer an acknowledgement.\n *\n * It decides nothing: it does not check the order, nor exclude any product\n * or service. Whether the right applies is the tenant's decision, explained on\n * its own withdrawal page (`LegalDocument` over `legal.withdrawalPolicy`).\n *\n * Stateful (client-only — ships from `./client`) but transport-agnostic, like\n * `DataRequestForm`:\n * - `getCaptchaToken` mints the reCAPTCHA v3 token; `undefined` blocks the\n * submission (base-api rejects a request without one).\n * - `submit` sends the body. By default it POSTs JSON to the SITE's own Route\n * Handler (`endpoint`, default `/api/withdrawal-requests`) and unwraps the\n * `{ data }` envelope — the API key never reaches the browser.\n *\n * On success the form is replaced by a receipt. When the acknowledgement email\n * could not be sent, the receipt repeats what was communicated so the buyer\n * can keep it. Errors never show server text: they map by HTTP status to the\n * fixed (overridable) copy in `labels`.\n */\nexport type WithdrawalRequestSubmitResult =\n | { ok: true; data: WithdrawalRequestSubmission }\n /** `status` is the HTTP status, or `0` when the request never got an answer. */\n | { ok: false; status: number }\n\n/** The server's field limits (base-api `POST /api/site/withdrawal-requests`). */\nexport const WITHDRAWAL_REQUEST_FIELD_LIMITS = Object.freeze({\n orderReference: 100,\n email: 320,\n name: 200,\n items: 2000,\n reason: 2000,\n})\n\nexport interface WithdrawalRequestFormLabels {\n orderReferenceLabel: string\n nameLabel: string\n emailLabel: string\n itemsLabel: string\n itemsPlaceholder: string\n reasonLabel: string\n /** Small print under the fields, e.g. how the data is used. `null` hides it. */\n notice: ReactNode\n submit: string\n submitting: string\n orderReferenceRequired: string\n emailInvalid: string\n itemsRequired: string\n /** Receipt when the buyer was emailed a copy; receives the formatted received-at date. */\n success: (receivedAt: string) => ReactNode\n /** Receipt when no copy could be emailed; the communication is listed below it. */\n successWithoutEmail: (receivedAt: string) => ReactNode\n receiptOrderReference: string\n receiptItems: string\n captchaError: string\n validationError: string\n rateLimitError: string\n genericError: string\n}\n\nexport const DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS: WithdrawalRequestFormLabels = {\n orderReferenceLabel: 'Número de pedido',\n nameLabel: 'Nombre y apellidos (opcional)',\n emailLabel: 'Email',\n itemsLabel: 'Qué productos o servicios quieres devolver o cancelar',\n itemsPlaceholder: 'Indica los productos, bonos o cursos y las unidades',\n reasonLabel: 'Motivo (opcional)',\n notice: 'Usaremos estos datos solo para gestionar tu desistimiento.',\n submit: 'Enviar desistimiento',\n submitting: 'Enviando…',\n orderReferenceRequired: 'Indica el número de pedido.',\n emailInvalid: 'Introduce un email válido.',\n itemsRequired: 'Indica de qué productos o servicios desistes.',\n success: (receivedAt) => (\n <>Hemos recibido tu desistimiento el {receivedAt}. Te hemos enviado una copia por email.</>\n ),\n successWithoutEmail: (receivedAt) => (\n <>\n Hemos recibido tu desistimiento el {receivedAt}. Guarda esta pantalla como justificante de lo\n que nos has comunicado:\n </>\n ),\n receiptOrderReference: 'Pedido',\n receiptItems: 'Productos o servicios',\n captchaError:\n 'No hemos podido verificar que no eres un robot. Recarga la página e inténtalo de nuevo.',\n validationError: 'Revisa los datos del formulario e inténtalo de nuevo.',\n rateLimitError: 'Has enviado demasiadas solicitudes. Espera unos minutos e inténtalo de nuevo.',\n genericError: 'No hemos podido enviar tu desistimiento. Inténtalo de nuevo más tarde.',\n}\n\nexport interface WithdrawalRequestFormProps {\n /** Mints the reCAPTCHA v3 token; `undefined` blocks the submission. */\n getCaptchaToken: () => Promise<string | undefined>\n /** Sends the body. Defaults to a JSON POST to `endpoint`. */\n submit?: (input: WithdrawalRequestInput) => Promise<WithdrawalRequestSubmitResult>\n /** The SITE's Route Handler used by the default `submit`. @default '/api/withdrawal-requests' */\n endpoint?: string\n /** First interaction with the form — wire it to the deferred reCAPTCHA `arm`. */\n onInteract?: () => void\n /** Called with the acknowledgement after a successful submission. */\n onSubmitted?: (ack: WithdrawalRequestSubmission) => void\n labels?: Partial<WithdrawalRequestFormLabels>\n /** Locale for the received-at date. @default 'es-ES' */\n locale?: string\n className?: string\n}\n\ntype FieldName = 'orderReference' | 'email' | 'items'\ntype FieldErrors = Partial<Record<FieldName, string>>\n\n/** Form order — focus goes to the first invalid field in this order. */\nconst FIELD_ORDER: readonly FieldName[] = ['orderReference', 'email', 'items']\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nasync function postToRoute(\n endpoint: string,\n input: WithdrawalRequestInput,\n): Promise<WithdrawalRequestSubmitResult> {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) return { ok: false, status: res.status }\n const json = (await res.json()) as { data?: WithdrawalRequestSubmission }\n return json.data ? { ok: true, data: json.data } : { ok: false, status: res.status }\n } catch {\n return { ok: false, status: 0 }\n }\n}\n\nfunction rejectionMessage(status: number, t: WithdrawalRequestFormLabels): string {\n if (status === 400) return t.validationError\n if (status === 422) return t.captchaError\n if (status === 429) return t.rateLimitError\n return t.genericError\n}\n\nconst FIELD_CLASS =\n 'w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20'\n\ninterface Receipt {\n ack: WithdrawalRequestSubmission\n orderReference: string\n items: string\n}\n\nexport function WithdrawalRequestForm({\n getCaptchaToken,\n submit,\n endpoint = '/api/withdrawal-requests',\n onInteract,\n onSubmitted,\n labels,\n locale = 'es-ES',\n className,\n}: WithdrawalRequestFormProps) {\n const t: WithdrawalRequestFormLabels = { ...DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS, ...labels }\n const id = useId()\n const [orderReference, setOrderReference] = useState('')\n const [name, setName] = useState('')\n const [email, setEmail] = useState('')\n const [items, setItems] = useState('')\n const [reason, setReason] = useState('')\n const [errors, setErrors] = useState<FieldErrors>({})\n const [formError, setFormError] = useState<string | null>(null)\n const [submitting, setSubmitting] = useState(false)\n const [receipt, setReceipt] = useState<Receipt | null>(null)\n const statusRef = useRef<HTMLDivElement>(null)\n\n // The receipt replaces the form: move focus to the status it announces.\n useEffect(() => {\n if (receipt) statusRef.current?.focus()\n }, [receipt])\n\n async function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault()\n if (submitting) return\n setFormError(null)\n\n const next: FieldErrors = {}\n if (!orderReference.trim()) next.orderReference = t.orderReferenceRequired\n if (!EMAIL_PATTERN.test(email.trim())) next.email = t.emailInvalid\n if (!items.trim()) next.items = t.itemsRequired\n setErrors(next)\n const firstInvalid = FIELD_ORDER.find((field) => next[field])\n if (firstInvalid) {\n document.getElementById(`${id}-${firstInvalid}`)?.focus()\n return\n }\n\n setSubmitting(true)\n try {\n const captcha = await getCaptchaToken().catch(() => undefined)\n if (!captcha) {\n setFormError(t.captchaError)\n return\n }\n const input: WithdrawalRequestInput = {\n orderReference: orderReference.trim(),\n email: email.trim(),\n ...(name.trim() ? { name: name.trim() } : {}),\n items: items.trim(),\n ...(reason.trim() ? { reason: reason.trim() } : {}),\n captcha,\n }\n const result = await (submit ?? ((body) => postToRoute(endpoint, body)))(input).catch(\n (): WithdrawalRequestSubmitResult => ({ ok: false, status: 0 }),\n )\n if (!result.ok) {\n setFormError(rejectionMessage(result.status, t))\n return\n }\n setReceipt({ ack: result.data, orderReference: input.orderReference, items: input.items })\n onSubmitted?.(result.data)\n } finally {\n setSubmitting(false)\n }\n }\n\n if (receipt) {\n const receivedAt = new Intl.DateTimeFormat(locale, {\n dateStyle: 'long',\n timeStyle: 'short',\n }).format(new Date(receipt.ack.receivedAt))\n return (\n <div\n ref={statusRef}\n tabIndex={-1}\n role='status'\n data-slot='withdrawal-request-form-success'\n className={cn('space-y-3 rounded-lg border border-border p-6 text-sm', className)}\n >\n {receipt.ack.acknowledgementSent ? (\n <p>{t.success(receivedAt)}</p>\n ) : (\n <>\n <p>{t.successWithoutEmail(receivedAt)}</p>\n <dl className='space-y-1'>\n <div>\n <dt className='font-medium'>{t.receiptOrderReference}</dt>\n <dd>{receipt.orderReference}</dd>\n </div>\n <div>\n <dt className='font-medium'>{t.receiptItems}</dt>\n <dd className='whitespace-pre-line'>{receipt.items}</dd>\n </div>\n </dl>\n </>\n )}\n </div>\n )\n }\n\n const errorId = (field: FieldName) => (errors[field] ? `${id}-${field}-error` : undefined)\n const fieldError = (field: FieldName) =>\n errors[field] ? (\n <p id={`${id}-${field}-error`} className='text-xs text-destructive'>\n {errors[field]}\n </p>\n ) : null\n const borderFor = (field: FieldName) => (errors[field] ? 'border-destructive' : 'border-border')\n\n return (\n <form\n noValidate\n onSubmit={handleSubmit}\n onFocusCapture={onInteract}\n onPointerDown={onInteract}\n data-slot='withdrawal-request-form'\n className={cn('space-y-4', className)}\n >\n <div className='space-y-1'>\n <label htmlFor={`${id}-orderReference`} className='text-sm font-medium'>\n {t.orderReferenceLabel}\n </label>\n <input\n id={`${id}-orderReference`}\n type='text'\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.orderReference}\n value={orderReference}\n onChange={(e) => setOrderReference(e.target.value)}\n aria-invalid={errors.orderReference ? true : undefined}\n aria-describedby={errorId('orderReference')}\n className={cn(FIELD_CLASS, borderFor('orderReference'))}\n />\n {fieldError('orderReference')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-name`} className='text-sm font-medium'>\n {t.nameLabel}\n </label>\n <input\n id={`${id}-name`}\n type='text'\n autoComplete='name'\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.name}\n value={name}\n onChange={(e) => setName(e.target.value)}\n className={cn(FIELD_CLASS, 'border-border')}\n />\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-email`} className='text-sm font-medium'>\n {t.emailLabel}\n </label>\n <input\n id={`${id}-email`}\n type='email'\n autoComplete='email'\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.email}\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n aria-invalid={errors.email ? true : undefined}\n aria-describedby={errorId('email')}\n className={cn(FIELD_CLASS, borderFor('email'))}\n />\n {fieldError('email')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-items`} className='text-sm font-medium'>\n {t.itemsLabel}\n </label>\n <textarea\n id={`${id}-items`}\n rows={3}\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.items}\n value={items}\n onChange={(e) => setItems(e.target.value)}\n placeholder={t.itemsPlaceholder}\n aria-invalid={errors.items ? true : undefined}\n aria-describedby={errorId('items')}\n className={cn(FIELD_CLASS, borderFor('items'))}\n />\n {fieldError('items')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-reason`} className='text-sm font-medium'>\n {t.reasonLabel}\n </label>\n <textarea\n id={`${id}-reason`}\n rows={3}\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.reason}\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n className={cn(FIELD_CLASS, 'border-border')}\n />\n </div>\n\n {t.notice ? <p className='text-xs text-muted-foreground'>{t.notice}</p> : null}\n\n {formError && (\n <p role='alert' className='text-sm text-destructive'>\n {formError}\n </p>\n )}\n\n <button\n type='submit'\n disabled={submitting}\n className='cursor-pointer rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:cursor-not-allowed disabled:opacity-60'\n >\n {submitting ? t.submitting : t.submit}\n </button>\n </form>\n )\n}\n","import { useId, useState, type FormEvent, type ReactNode } from 'react'\nimport { cn } from '@reblu/site-ui/utils'\nimport type {\n AcademyCheckoutErrorCode,\n AcademyCheckoutRequest,\n AcademyCheckoutSession,\n AcademyCourseDetail,\n ShopCheckoutCopy,\n} from '@reblu/site-contracts'\nimport { formatPrice } from '../lib/format-price'\nimport { toAcademyCheckoutInvoiceFields } from '../lib/course-guest-checkout'\nimport { CheckoutPayAction } from './checkout-pay-action'\nimport {\n CheckoutTermsAcceptance,\n toCheckoutTermsFields,\n type CheckoutTermsCopy,\n} from './checkout-terms-acceptance'\nimport {\n EMPTY_FULL_INVOICE_VALUE,\n FullInvoiceFields,\n isOverSimplifiedInvoiceCap,\n type FullInvoiceErrors,\n type FullInvoiceFieldsLabels,\n type FullInvoiceValue,\n} from './full-invoice-fields'\n\n/**\n * `CourseGuestCheckoutForm` (REB-1264) — drop-in guest checkout of a course\n * that needs no validation, over `POST /api/site/academy/checkout`: name,\n * surname, email, the tenant's terms box, the course's consent box\n * (`AcademyCourseDetail.withdrawalConsent`), the optional full invoice, the\n * total and the pay notice. On success the buyer goes to Stripe; the account\n * is created after payment.\n *\n * Stateful (client-only — ships from `./client`) but transport-agnostic, like\n * `WithdrawalRequestForm`: `submit` sends the body; by default it POSTs JSON\n * to the SITE's own Route Handler (`endpoint`, default `/api/academy/checkout`\n * — mount `createAcademyCheckoutRoute()` from `@reblu/site-next/route-handlers`\n * there), which adds the return URLs and the API key.\n *\n * When the server answers `ENROLLMENT_REQUEST_REQUIRED` (the course needs a\n * validation, or is free), the form gives way to `requestForm`, the SITE's\n * enrollment request form. Render `requestForm` straight away for a course\n * `courseSellsAsGuest` rejects.\n *\n * No enumeration: the form says the same for every email and never mentions\n * an account (REB-834). The copy is Spanish and neutral; replace any text\n * with `labels` / `errorMessages`.\n */\nexport type CourseGuestCheckoutInput = Omit<AcademyCheckoutRequest, 'successUrl' | 'cancelUrl'> &\n Partial<Pick<AcademyCheckoutRequest, 'successUrl' | 'cancelUrl'>>\n\nexport type CourseGuestCheckoutResult =\n | { ok: true; data: AcademyCheckoutSession }\n /** `status` is the HTTP status, or `0` when the request never got an answer. */\n | { ok: false; status: number; code?: string; details?: unknown }\n\n/** The tenant's checkout copy (`ShopConfig.checkout`): terms box, button, notice and invoice texts. */\nexport type CourseGuestCheckoutCopy = CheckoutTermsCopy &\n Partial<\n Pick<\n ShopCheckoutCopy,\n 'continueToPaymentLabel' | 'payNotice' | 'fullInvoiceCheckboxLabel' | 'fullInvoiceHelpText'\n >\n >\n\nexport interface CourseGuestCheckoutFormLabels {\n firstNameLabel: string\n lastNameLabel: string\n emailLabel: string\n /** The total line; receives the formatted price. */\n total: (formattedTotal: string) => ReactNode\n /** Button text when the copy carries none. */\n submit: string\n submitting: string\n firstNameRequired: string\n lastNameRequired: string\n emailInvalid: string\n termsRequired: string\n consentRequired: string\n /** Shown above `requestForm` after `ENROLLMENT_REQUEST_REQUIRED`. `null` hides it. */\n requestRequiredNotice: ReactNode\n genericError: string\n}\n\nexport const DEFAULT_COURSE_GUEST_CHECKOUT_FORM_LABELS: CourseGuestCheckoutFormLabels = {\n firstNameLabel: 'Nombre',\n lastNameLabel: 'Apellidos',\n emailLabel: 'Email',\n total: (total) => `Total (impuestos incluidos): ${total}`,\n submit: 'Continuar al pago',\n submitting: 'Procesando…',\n firstNameRequired: 'Indica tu nombre.',\n lastNameRequired: 'Indica tus apellidos.',\n emailInvalid: 'Introduce un email válido.',\n termsRequired: 'Para continuar, acepta las condiciones.',\n consentRequired: 'Para continuar, marca esta casilla.',\n requestRequiredNotice: 'Este curso se solicita antes de pagar. Completa la solicitud.',\n genericError: 'No hemos podido iniciar el pago. Inténtalo de nuevo.',\n}\n\n/** Default copy for every `AcademyCheckoutErrorCode`; override any subset with `errorMessages`. */\nexport const DEFAULT_COURSE_GUEST_CHECKOUT_ERROR_MESSAGES: Record<\n AcademyCheckoutErrorCode,\n string\n> = {\n INVALID_INPUT: 'Revisa los datos introducidos.',\n RATE_LIMITED: 'Demasiados intentos. Espera unos minutos e inténtalo de nuevo.',\n COURSE_NOT_FOUND: 'Este curso ya no está disponible.',\n SALES_CLOSED: 'Este curso no está a la venta en este momento.',\n ENROLLMENT_REQUEST_REQUIRED: 'Este curso se solicita antes de pagar.',\n INVOICING_NOT_READY: 'No se puede completar la compra en este momento. Inténtalo más tarde.',\n TERMS_NOT_ACCEPTED: 'Para continuar, acepta las condiciones.',\n TERMS_VERSION_CHANGED:\n 'Las condiciones se han actualizado. Recarga la página y vuelve a aceptarlas.',\n CONSENT_REQUIRED: 'Para continuar, marca la casilla del curso.',\n CONSENT_VERSION_CHANGED:\n 'El texto de la casilla ha cambiado. Recarga la página y vuelve a marcarla.',\n INVALID_BILLING: 'Revisa los datos de facturación.',\n}\n\nexport interface CourseGuestCheckoutFormProps {\n /**\n * The fields of `AcademyCourseDetail` the form reads. `withdrawalConsent` is\n * REQUIRED (pass `null` when the course has no box): omitting it for a\n * course that has one would send no version, and the server would refuse\n * every try with `CONSENT_VERSION_CHANGED`. The contract field is optional\n * (older APIs), so pass `course.withdrawalConsent ?? null`.\n */\n course: Pick<AcademyCourseDetail, 'slug' | 'priceCents'> & {\n withdrawalConsent: NonNullable<AcademyCourseDetail['withdrawalConsent']> | null\n }\n /** The tenant's checkout copy — pass `ShopConfig.checkout`. */\n copy: CourseGuestCheckoutCopy\n /** The SITE's terms page, used when the tenant set no `termsLinkUrl`. */\n termsHref: string\n /** The SITE's enrollment request form, shown on `ENROLLMENT_REQUEST_REQUIRED`. */\n requestForm: ReactNode\n /** `false` when the tenant issues no invoices: no full-invoice opt-in. @default true */\n issuesInvoices?: boolean\n /** The tenant's simplified-invoice cap in cents (`ShopConfig.simplifiedInvoiceCapCents`). */\n simplifiedInvoiceCapCents?: number | null\n /** Sends the body. Defaults to a JSON POST to `endpoint`. */\n submit?: (input: CourseGuestCheckoutInput) => Promise<CourseGuestCheckoutResult>\n /** The SITE's Route Handler used by the default `submit`. @default '/api/academy/checkout' */\n endpoint?: string\n /** Called with the Stripe URL. @default `window.location.assign(url)` */\n onRedirect?: (url: string) => void\n /** Called with every refusal, e.g. to reload the page on a `*_VERSION_CHANGED`. */\n onError?: (result: Extract<CourseGuestCheckoutResult, { ok: false }>) => void\n labels?: Partial<CourseGuestCheckoutFormLabels>\n errorMessages?: Partial<Record<AcademyCheckoutErrorCode, string>>\n invoiceLabels?: Partial<FullInvoiceFieldsLabels>\n invoiceCountries?: ReadonlyArray<{ code: string; name: string }>\n currencyCode?: string\n locale?: string\n className?: string\n}\n\ntype FieldName = 'firstName' | 'lastName' | 'email' | 'terms' | 'consent'\ntype FieldErrors = Partial<Record<FieldName, string>>\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nconst FIELD_CLASS =\n 'w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20'\n\nasync function postToRoute(\n endpoint: string,\n input: CourseGuestCheckoutInput,\n): Promise<CourseGuestCheckoutResult> {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n const json = (await res.json().catch(() => null)) as {\n data?: AcademyCheckoutSession\n code?: unknown\n details?: unknown\n } | null\n if (res.ok && json?.data) return { ok: true, data: json.data }\n return {\n ok: false,\n status: res.status,\n ...(typeof json?.code === 'string' ? { code: json.code } : {}),\n ...(json?.details !== undefined ? { details: json.details } : {}),\n }\n } catch {\n return { ok: false, status: 0 }\n }\n}\n\n/** `details.fieldErrors` (one string per field) of a refusal, or `{}`. */\nfunction fieldErrorsOf(details: unknown): Record<string, string> {\n const errors = (details as { fieldErrors?: unknown } | null)?.fieldErrors\n if (typeof errors !== 'object' || errors === null) return {}\n return Object.fromEntries(\n Object.entries(errors).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n )\n}\n\nfunction toInvoiceErrors(fieldErrors: Record<string, string>): FullInvoiceErrors {\n const out: FullInvoiceErrors = {}\n for (const key of ['billingIdType', 'billingIdCountry', 'billingNif', 'billingName'] as const) {\n if (fieldErrors[key]) out[key] = fieldErrors[key]\n }\n if (fieldErrors.billingAddress) out.address = fieldErrors.billingAddress\n return out\n}\n\nexport function CourseGuestCheckoutForm({\n course,\n copy,\n termsHref,\n requestForm,\n issuesInvoices = true,\n simplifiedInvoiceCapCents = null,\n submit,\n endpoint = '/api/academy/checkout',\n onRedirect,\n onError,\n labels,\n errorMessages,\n invoiceLabels,\n invoiceCountries,\n currencyCode,\n locale,\n className,\n}: CourseGuestCheckoutFormProps) {\n const t: CourseGuestCheckoutFormLabels = {\n ...DEFAULT_COURSE_GUEST_CHECKOUT_FORM_LABELS,\n ...labels,\n }\n const messages = { ...DEFAULT_COURSE_GUEST_CHECKOUT_ERROR_MESSAGES, ...errorMessages }\n const id = useId()\n const [firstName, setFirstName] = useState('')\n const [lastName, setLastName] = useState('')\n const [email, setEmail] = useState('')\n const [acceptsTerms, setAcceptsTerms] = useState(false)\n const [acceptsConsent, setAcceptsConsent] = useState(false)\n const [invoice, setInvoice] = useState<FullInvoiceValue>(EMPTY_FULL_INVOICE_VALUE)\n const [errors, setErrors] = useState<FieldErrors>({})\n const [invoiceErrors, setInvoiceErrors] = useState<FullInvoiceErrors>({})\n const [formError, setFormError] = useState<string | null>(null)\n const [submitting, setSubmitting] = useState(false)\n const [needsRequest, setNeedsRequest] = useState(false)\n\n const consent = course.withdrawalConsent\n const hasTerms = Boolean(copy.termsVersion)\n const overCap = isOverSimplifiedInvoiceCap(course.priceCents, simplifiedInvoiceCapCents)\n const formattedTotal = formatPrice(course.priceCents, currencyCode, locale)\n const notice = copy.payNotice\n ? copy.payNotice.replaceAll('{total}', formattedTotal).replaceAll('{tenant}', copy.tenantName)\n : null\n\n if (needsRequest) {\n return (\n <div data-slot='course-guest-checkout-request' className={cn('space-y-4', className)}>\n {t.requestRequiredNotice ? (\n <p className='text-sm text-muted-foreground'>{t.requestRequiredNotice}</p>\n ) : null}\n {requestForm}\n </div>\n )\n }\n\n async function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault()\n if (submitting) return\n setFormError(null)\n setInvoiceErrors({})\n\n const next: FieldErrors = {}\n if (!firstName.trim()) next.firstName = t.firstNameRequired\n if (!lastName.trim()) next.lastName = t.lastNameRequired\n if (!EMAIL_PATTERN.test(email.trim())) next.email = t.emailInvalid\n if (hasTerms && !acceptsTerms) next.terms = t.termsRequired\n if (consent && !acceptsConsent) next.consent = t.consentRequired\n setErrors(next)\n if (Object.keys(next).length > 0) return\n\n const input: CourseGuestCheckoutInput = {\n courseSlug: course.slug,\n firstName: firstName.trim(),\n lastName: lastName.trim(),\n email: email.trim(),\n ...toCheckoutTermsFields(copy, acceptsTerms),\n ...(consent\n ? { acceptsWithdrawalWaiver: acceptsConsent, withdrawalWaiverVersion: consent.version }\n : {}),\n ...toAcademyCheckoutInvoiceFields(invoice, overCap, issuesInvoices),\n }\n\n setSubmitting(true)\n try {\n const result = await (submit ?? ((body) => postToRoute(endpoint, body)))(input).catch(\n (): CourseGuestCheckoutResult => ({ ok: false, status: 0 }),\n )\n if (result.ok) {\n ;(onRedirect ?? ((url: string) => window.location.assign(url)))(result.data.url)\n return\n }\n onError?.(result)\n if (result.code === 'ENROLLMENT_REQUEST_REQUIRED') {\n setNeedsRequest(true)\n return\n }\n if (result.code === 'INVALID_BILLING')\n setInvoiceErrors(toInvoiceErrors(fieldErrorsOf(result.details)))\n const code = result.code as AcademyCheckoutErrorCode | undefined\n setFormError((code && messages[code]) || t.genericError)\n } finally {\n setSubmitting(false)\n }\n }\n\n const fieldError = (field: FieldName) =>\n errors[field] ? (\n <p id={`${id}-${field}-error`} className='text-xs text-destructive'>\n {errors[field]}\n </p>\n ) : null\n const invalid = (field: FieldName) =>\n errors[field]\n ? { 'aria-invalid': true as const, 'aria-describedby': `${id}-${field}-error` }\n : {}\n const border = (field: FieldName) => (errors[field] ? 'border-destructive' : 'border-border')\n\n const textField = (\n field: 'firstName' | 'lastName' | 'email',\n label: string,\n value: string,\n onChange: (value: string) => void,\n autoComplete: string,\n ) => (\n <div className='space-y-1'>\n <label htmlFor={`${id}-${field}`} className='text-sm font-medium'>\n {label}\n </label>\n <input\n id={`${id}-${field}`}\n type={field === 'email' ? 'email' : 'text'}\n autoComplete={autoComplete}\n maxLength={field === 'email' ? 254 : 200}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n {...invalid(field)}\n className={cn(FIELD_CLASS, border(field))}\n />\n {fieldError(field)}\n </div>\n )\n\n return (\n <form\n noValidate\n onSubmit={handleSubmit}\n data-slot='course-guest-checkout-form'\n className={cn('space-y-6', className)}\n >\n <div className='grid grid-cols-1 gap-4 sm:grid-cols-2'>\n {textField('firstName', t.firstNameLabel, firstName, setFirstName, 'given-name')}\n {textField('lastName', t.lastNameLabel, lastName, setLastName, 'family-name')}\n <div className='sm:col-span-2'>\n {textField('email', t.emailLabel, email, setEmail, 'email')}\n </div>\n </div>\n\n <FullInvoiceFields\n value={invoice}\n onChange={setInvoice}\n overCap={overCap}\n issuesInvoices={issuesInvoices}\n errors={invoiceErrors}\n countries={invoiceCountries}\n labels={{\n ...(copy.fullInvoiceCheckboxLabel ? { toggle: copy.fullInvoiceCheckboxLabel } : {}),\n ...(copy.fullInvoiceHelpText ? { underCapNotice: copy.fullInvoiceHelpText } : {}),\n ...invoiceLabels,\n }}\n />\n\n <div className='space-y-3'>\n <CheckoutTermsAcceptance\n copy={copy}\n termsHref={termsHref}\n checked={acceptsTerms}\n onCheckedChange={setAcceptsTerms}\n error={errors.terms}\n id={`${id}-terms`}\n />\n {consent ? (\n <div\n data-slot='course-guest-checkout-consent'\n data-kind={consent.kind}\n className='space-y-1'\n >\n <div className='flex items-start gap-3'>\n <input\n id={`${id}-consent`}\n type='checkbox'\n checked={acceptsConsent}\n onChange={(e) => setAcceptsConsent(e.target.checked)}\n {...invalid('consent')}\n className='mt-0.5 size-5 shrink-0 cursor-pointer accent-primary'\n />\n <label htmlFor={`${id}-consent`} className='text-sm leading-relaxed'>\n {consent.text}\n </label>\n </div>\n {errors.consent ? (\n <p id={`${id}-consent-error`} className='pl-8 text-sm text-destructive'>\n {errors.consent}\n </p>\n ) : null}\n </div>\n ) : null}\n </div>\n\n <p data-slot='course-guest-checkout-total' className='text-base font-semibold'>\n {t.total(formattedTotal)}\n </p>\n\n {formError ? (\n <p role='alert' className='text-sm text-destructive'>\n {formError}\n </p>\n ) : null}\n\n <CheckoutPayAction\n label={copy.continueToPaymentLabel ?? t.submit}\n notice={notice}\n submitting={submitting}\n submittingLabel={t.submitting}\n id={`${id}-pay`}\n />\n </form>\n )\n}\n","import type {\n AcademyCheckoutBilling,\n AcademyCheckoutRequest,\n AcademyCourseDetail,\n} from '@reblu/site-contracts'\nimport type { FullInvoiceValue } from '../components/full-invoice-fields'\n\n/**\n * REB-1264 — pure, RSC-safe helpers of the guest course checkout\n * (`CourseGuestCheckoutForm`, `@reblu/site-blocks/client`). They ship from\n * the root entry so a Server Component can call them.\n */\n\n/**\n * Whether the course is bought in one step by a guest\n * (`POST /api/site/academy/checkout`): on sale, needing no validation and not\n * free. Otherwise keep the enrollment request flow. The server decides in the\n * end — it answers `ENROLLMENT_REQUEST_REQUIRED` when this is out of date.\n */\nexport function courseSellsAsGuest(\n course: Pick<AcademyCourseDetail, 'salesEnabled' | 'requiresValidation' | 'priceCents'>,\n): boolean {\n return course.salesEnabled && !course.requiresValidation && course.priceCents > 0\n}\n\n/**\n * The guest checkout body fields for the full-invoice request — the academy\n * twin of `toCheckoutInvoiceFields` (its `billingAddress` has no\n * first/last name). Empty when the buyer asks for no full invoice within the\n * cap, and always empty when the tenant issues no invoices.\n */\nexport function toAcademyCheckoutInvoiceFields(\n value: FullInvoiceValue,\n overCap: boolean,\n issuesInvoices = true,\n): Pick<AcademyCheckoutRequest, 'wantsFullInvoice' | 'billing'> {\n if (!issuesInvoices) return {}\n if (!value.wantsFullInvoice && !overCap) return {}\n const billing: AcademyCheckoutBilling = {\n billingName: value.billingName.trim(),\n billingNif: value.billingNif.trim(),\n billingIdType: value.billingIdType,\n ...(value.billingIdType === 'NIF' ? {} : { billingIdCountry: value.billingIdCountry }),\n billingAddress: {\n street: value.street.trim(),\n postalCode: value.postalCode.trim(),\n city: value.city.trim(),\n country: value.country,\n },\n }\n return { ...(value.wantsFullInvoice ? { wantsFullInvoice: true } : {}), billing }\n}\n","import { cn } from '@reblu/site-ui/utils'\n\n/**\n * `CheckoutPayAction` (REB-1198) — the last control of the single-step\n * checkout: the submit button (`ShopCheckoutCopy.continueToPaymentLabel`,\n * «Continuar al pago») and, right under it and readable (lawyer, 3-Nov: no\n * tiny text), the notice with the amount (`ShopQuote.payNotice`, «En el\n * siguiente paso pagarás {total} en la pasarela segura de Stripe…», already\n * filled). The button is described by the notice, so a screen reader hears\n * both. Place it inside the checkout `<form>`.\n */\nexport interface CheckoutPayActionProps {\n /** The tenant's button text: `ShopCheckoutCopy.continueToPaymentLabel`. */\n label: string\n /** The tenant's small print: `ShopQuote.payNotice`. Omit while there is no quote. */\n notice?: string | null\n submitting?: boolean\n /** Shown on the button while `submitting`. @default 'Procesando…' */\n submittingLabel?: string\n disabled?: boolean\n /** Prefix of the element ids (one action per page). @default 'checkout-pay' */\n id?: string\n className?: string\n /** Classes for the button; REPLACE the default look. */\n buttonClassName?: string\n /** Classes for the notice; REPLACE the default `text-sm text-muted-foreground`. */\n noticeClassName?: string\n}\n\nexport function CheckoutPayAction({\n label,\n notice,\n submitting = false,\n submittingLabel = 'Procesando…',\n disabled = false,\n id = 'checkout-pay',\n className,\n buttonClassName,\n noticeClassName,\n}: CheckoutPayActionProps) {\n const noticeId = `${id}-notice`\n const isDisabled = submitting || disabled\n return (\n <div data-slot='checkout-pay-action' className={cn('space-y-3', className)}>\n <button\n type='submit'\n disabled={isDisabled}\n aria-busy={submitting || undefined}\n aria-describedby={notice ? noticeId : undefined}\n className={\n buttonClassName ??\n cn(\n 'flex w-full items-center justify-center gap-2 rounded-xl px-4 py-3 text-base font-semibold transition-all',\n isDisabled\n ? 'cursor-not-allowed bg-muted text-muted-foreground'\n : 'cursor-pointer bg-primary text-primary-foreground hover:bg-primary/90',\n )\n }\n >\n {submitting ? submittingLabel : label}\n </button>\n {notice ? (\n <p id={noticeId} data-slot='checkout-pay-notice' className={noticeClassName ?? 'text-sm text-muted-foreground'}>\n {notice}\n </p>\n ) : null}\n </div>\n )\n}\n","import type { ShopCheckoutCopy, ShopCheckoutInput } from '@reblu/site-contracts'\nimport { cn } from '@reblu/site-ui/utils'\n\n/**\n * `CheckoutTermsAcceptance` (REB-1198) — the mandatory, unticked box «He leído\n * y acepto las {terms} de {tenant}» of the single-step checkout. Every text is\n * the tenant's (`ShopConfig.checkout`, edited in nexus › Configuración ›\n * Legal): `{terms}` becomes the link to the full document (text\n * `termsLinkText`, target `termsLinkUrl` or the SITE's own `termsHref`, a new\n * tab when `isTermsLinkNewTab`), `{tenant}` the site name. A label without\n * `{terms}` gets the link appended.\n *\n * Renders nothing while the tenant has published no terms\n * (`termsVersion: null`): there is nothing to accept and checkout does not\n * ask. Send {@link toCheckoutTermsFields} with the checkout body; the server\n * refuses without it (`TERMS_NOT_ACCEPTED`) or with an old version\n * (`TERMS_VERSION_CHANGED`: reload the config and ask again).\n */\nexport type CheckoutTermsCopy = Pick<\n ShopCheckoutCopy,\n 'tenantName' | 'termsCheckboxLabel' | 'termsLinkText' | 'termsLinkUrl' | 'isTermsLinkNewTab' | 'termsVersion'\n>\n\nexport interface CheckoutTermsAcceptanceProps {\n copy: CheckoutTermsCopy\n /** The SITE's route to its terms page, used when the tenant set no `termsLinkUrl`. */\n termsHref: string\n checked: boolean\n onCheckedChange: (checked: boolean) => void\n /** E.g. «Debes aceptar las condiciones para continuar.» (the SITE's or the API's text). */\n error?: string | null\n /** Appended inside the link when it opens a new tab (screen readers). @default '(se abre en una pestaña nueva)' */\n newTabHint?: string\n /** The checkbox id (one box per page). @default 'checkout-terms' */\n id?: string\n className?: string\n}\n\n/** Splits a label around `{terms}`; no placeholder = the link goes at the end. */\nfunction splitTermsLabel(label: string): [string, string] {\n const at = label.indexOf('{terms}')\n return at === -1 ? [`${label} `, ''] : [label.slice(0, at), label.slice(at + '{terms}'.length)]\n}\n\nexport function CheckoutTermsAcceptance({\n copy,\n termsHref,\n checked,\n onCheckedChange,\n error,\n newTabHint = '(se abre en una pestaña nueva)',\n id,\n className,\n}: CheckoutTermsAcceptanceProps) {\n if (!copy.termsVersion) return null\n const boxId = id ?? 'checkout-terms'\n const errorId = `${boxId}-error`\n const [before, after] = splitTermsLabel(copy.termsCheckboxLabel).map((part) =>\n part.replaceAll('{tenant}', copy.tenantName),\n )\n const newTab = copy.isTermsLinkNewTab\n return (\n <div data-slot='checkout-terms' className={cn('space-y-1', className)}>\n <div className='flex items-start gap-3'>\n <input\n id={boxId}\n type='checkbox'\n required\n checked={checked}\n onChange={(e) => onCheckedChange(e.target.checked)}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className='mt-0.5 size-5 shrink-0 cursor-pointer accent-primary'\n />\n <label htmlFor={boxId} className='text-sm leading-relaxed'>\n {before}\n <a\n href={copy.termsLinkUrl ?? termsHref}\n {...(newTab ? { target: '_blank', rel: 'noopener noreferrer' } : {})}\n className='font-medium underline underline-offset-2 hover:text-primary'\n >\n {copy.termsLinkText}\n {newTab ? (\n <>\n {' '}\n <span className='sr-only'>{newTabHint}</span>\n </>\n ) : null}\n </a>\n {after}\n </label>\n </div>\n {error ? (\n <p id={errorId} className='pl-8 text-sm text-destructive'>\n {error}\n </p>\n ) : null}\n </div>\n )\n}\n\n/**\n * The checkout body fields for the terms box: `acceptsTerms` and the version\n * shown (`termsVersionId`), or nothing when the tenant published no terms.\n */\nexport function toCheckoutTermsFields(\n copy: Pick<ShopCheckoutCopy, 'termsVersion'> | null | undefined,\n accepted: boolean,\n): Pick<ShopCheckoutInput, 'acceptsTerms' | 'termsVersionId'> {\n const version = copy?.termsVersion\n return version ? { acceptsTerms: accepted, termsVersionId: version.id } : {}\n}\n","import { cn } from '@reblu/site-ui/utils'\nimport type { ShopBillingInput, ShopCheckoutInput } from '@reblu/site-contracts'\n\n/**\n * `FullInvoiceFields` — the shop checkout's full-invoice request, drop-in\n * (REB-73). Fully controlled (`value`/`onChange`), no hooks, no fetch: the\n * SITE owns the state and sends `toCheckoutInvoiceFields(value, overCap)`\n * with its checkout body.\n *\n * The rule it presents (owner decision 15-Oct-2026): «Si quieren factura\n * completa, si o si deben aportar el NIF, si no lo aportan se emite factura\n * simplificada». The identifier is a Spanish NIF/NIE/CIF or, for a foreign\n * buyer, an EU VAT id, a passport or an ID document of their country — with\n * the country that issued it. Over the shop's simplified-invoice cap\n * (`ShopConfig.simplifiedInvoiceCapCents`, see `isOverSimplifiedInvoiceCap`)\n * a full invoice is the only legal one: pass `overCap` and the fields become\n * required, without a toggle. The server validates the identifier and\n * refuses with `BILLING_DATA_REQUIRED` what this form cannot prevent.\n *\n * A shop that issues no invoices (`ShopConfig.simplifiedInvoiceCapCents` is\n * `null`, see `shopIssuesInvoices`) offers no opt-in: pass\n * `issuesInvoices={false}` and the block renders nothing — the server would\n * refuse `wantsFullInvoice: true` with `FULL_INVOICE_UNAVAILABLE`.\n */\nexport type FullInvoiceIdType = NonNullable<ShopBillingInput['billingIdType']>\n\nexport interface FullInvoiceValue {\n wantsFullInvoice: boolean\n billingIdType: FullInvoiceIdType\n /** ISO 3166-1 alpha-2 country that issued a foreign document. */\n billingIdCountry: string\n billingNif: string\n billingName: string\n street: string\n postalCode: string\n city: string\n /** ISO 3166-1 alpha-2 country of the billing address. */\n country: string\n}\n\nexport const EMPTY_FULL_INVOICE_VALUE: FullInvoiceValue = {\n wantsFullInvoice: false,\n billingIdType: 'NIF',\n billingIdCountry: '',\n billingNif: '',\n billingName: '',\n street: '',\n postalCode: '',\n city: '',\n country: 'ES',\n}\n\nexport type FullInvoiceErrors = Partial<\n Record<'billingIdType' | 'billingIdCountry' | 'billingNif' | 'billingName' | 'address', string>\n>\n\nexport interface FullInvoiceFieldsLabels {\n toggle: string\n underCapNotice: string\n overCapNotice: string\n idType: string\n idCountry: string\n chooseCountry: string\n spanishNif: string\n documentNumber: string\n name: string\n street: string\n postalCode: string\n city: string\n country: string\n idTypeOptions: Record<FullInvoiceIdType, string>\n}\n\nconst DEFAULT_LABELS: FullInvoiceFieldsLabels = {\n toggle: '¿Quieres factura completa?',\n underCapNotice:\n 'Para una factura completa necesitas tu NIF/NIE o un documento de identificación. Si no, recibirás una factura simplificada.',\n overCapNotice:\n 'El importe del pedido supera el máximo de la factura simplificada: para completar la compra, indica tu NIF/NIE o un documento de identificación, tu nombre y tu dirección de facturación.',\n idType: 'Tipo de documento',\n idCountry: 'País que expidió el documento',\n chooseCountry: 'Elige un país',\n spanishNif: 'NIF / NIE / CIF',\n documentNumber: 'Número de documento',\n name: 'Nombre o razón social',\n street: 'Dirección',\n postalCode: 'Código postal',\n city: 'Ciudad',\n country: 'País',\n idTypeOptions: {\n NIF: 'NIF / NIE / CIF (España)',\n '02': 'NIF-IVA intracomunitario (otro país de la UE)',\n '03': 'Pasaporte',\n '04': 'Documento oficial de identidad de tu país de residencia',\n '05': 'Certificado de residencia',\n '06': 'Otro documento acreditativo',\n },\n}\n\nconst ID_TYPES: FullInvoiceIdType[] = ['NIF', '02', '03', '04', '05', '06']\n\n/** ISO 3166-1 alpha-2 countries offered by default: Spain, the EU and frequent others. */\nexport const DEFAULT_INVOICE_COUNTRIES: ReadonlyArray<{ code: string; name: string }> = [\n { code: 'ES', name: 'España' },\n { code: 'DE', name: 'Alemania' },\n { code: 'AD', name: 'Andorra' },\n { code: 'AR', name: 'Argentina' },\n { code: 'AT', name: 'Austria' },\n { code: 'BE', name: 'Bélgica' },\n { code: 'BG', name: 'Bulgaria' },\n { code: 'CY', name: 'Chipre' },\n { code: 'CO', name: 'Colombia' },\n { code: 'HR', name: 'Croacia' },\n { code: 'DK', name: 'Dinamarca' },\n { code: 'SK', name: 'Eslovaquia' },\n { code: 'SI', name: 'Eslovenia' },\n { code: 'US', name: 'Estados Unidos' },\n { code: 'EE', name: 'Estonia' },\n { code: 'FI', name: 'Finlandia' },\n { code: 'FR', name: 'Francia' },\n { code: 'GR', name: 'Grecia' },\n { code: 'HU', name: 'Hungría' },\n { code: 'IE', name: 'Irlanda' },\n { code: 'IT', name: 'Italia' },\n { code: 'LV', name: 'Letonia' },\n { code: 'LT', name: 'Lituania' },\n { code: 'LU', name: 'Luxemburgo' },\n { code: 'MT', name: 'Malta' },\n { code: 'MA', name: 'Marruecos' },\n { code: 'MX', name: 'México' },\n { code: 'NL', name: 'Países Bajos' },\n { code: 'PL', name: 'Polonia' },\n { code: 'PT', name: 'Portugal' },\n { code: 'GB', name: 'Reino Unido' },\n { code: 'CZ', name: 'República Checa' },\n { code: 'RO', name: 'Rumanía' },\n { code: 'SE', name: 'Suecia' },\n { code: 'CH', name: 'Suiza' },\n { code: 'VE', name: 'Venezuela' },\n]\n\n/**\n * Whether the shop issues invoices at all, read from the field the contract\n * defines for it: `ShopConfig.simplifiedInvoiceCapCents` is the effective cap\n * while invoicing is on and `null` otherwise (absent on an older base-api,\n * which accepts no opt-in either).\n */\nexport function shopIssuesInvoices(config: { simplifiedInvoiceCapCents?: number | null }): boolean {\n return config.simplifiedInvoiceCapCents != null\n}\n\n/** Whether a cart total (cents) is over the shop's simplified-invoice cap; `null` cap = no gate. */\nexport function isOverSimplifiedInvoiceCap(totalCents: number, capCents: number | null): boolean {\n return capCents != null && totalCents > capCents\n}\n\n/**\n * The checkout body fields for the request: `wantsFullInvoice` and the\n * `billing` block (identifier, its type and country, name and address). Empty\n * when the buyer asks for no full invoice within the cap, and always empty\n * when the shop issues no invoices (`issuesInvoices` false).\n */\nexport function toCheckoutInvoiceFields(\n value: FullInvoiceValue,\n overCap: boolean,\n issuesInvoices = true,\n): Pick<ShopCheckoutInput, 'wantsFullInvoice' | 'billing'> {\n if (!issuesInvoices) return {}\n if (!value.wantsFullInvoice && !overCap) return {}\n const name = value.billingName.trim()\n const [first = '', ...rest] = name.split(/\\s+/)\n const billing: ShopBillingInput = {\n billingName: name,\n billingNif: value.billingNif.trim(),\n billingIdType: value.billingIdType,\n ...(value.billingIdType === 'NIF' ? {} : { billingIdCountry: value.billingIdCountry }),\n billingAddress: {\n firstName: first,\n lastName: rest.join(' ') || first,\n street: value.street.trim(),\n city: value.city.trim(),\n postalCode: value.postalCode.trim(),\n country: value.country,\n },\n }\n return { ...(value.wantsFullInvoice ? { wantsFullInvoice: true } : {}), billing }\n}\n\nexport interface FullInvoiceFieldsProps {\n value: FullInvoiceValue\n onChange: (next: FullInvoiceValue) => void\n /** The cart total is over `ShopConfig.simplifiedInvoiceCapCents`. */\n overCap: boolean\n /**\n * `false` when the shop issues no invoices (`shopIssuesInvoices(config)`):\n * the block renders nothing. Defaults to `true`.\n */\n issuesInvoices?: boolean\n errors?: FullInvoiceErrors\n countries?: ReadonlyArray<{ code: string; name: string }>\n labels?: Partial<FullInvoiceFieldsLabels>\n className?: string\n}\n\nconst FIELD_CLASS =\n 'w-full rounded-lg border border-border bg-background px-3 py-2 text-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20'\n\nexport function FullInvoiceFields({\n value,\n onChange,\n overCap,\n issuesInvoices = true,\n errors = {},\n countries = DEFAULT_INVOICE_COUNTRIES,\n labels,\n className,\n}: FullInvoiceFieldsProps) {\n if (!issuesInvoices) return null\n const t = { ...DEFAULT_LABELS, ...labels }\n const showFields = value.wantsFullInvoice || overCap\n const isSpanishNif = value.billingIdType === 'NIF'\n const set = <K extends keyof FullInvoiceValue>(key: K, next: FullInvoiceValue[K]) =>\n onChange({ ...value, [key]: next })\n\n const error = (key: keyof FullInvoiceErrors) =>\n errors[key] ? (\n <p id={`full-invoice-${key}-error`} className='text-sm text-destructive'>\n {errors[key]}\n </p>\n ) : null\n const invalid = (key: keyof FullInvoiceErrors) =>\n errors[key]\n ? { 'aria-invalid': true as const, 'aria-describedby': `full-invoice-${key}-error` }\n : {}\n\n return (\n <section className={cn('space-y-4', className)} data-slot='full-invoice-fields'>\n {overCap ? (\n <p role='note' className='rounded-lg border border-border p-4 text-sm'>\n {t.overCapNotice}\n </p>\n ) : (\n <div className='space-y-1'>\n <label className='flex items-center gap-2 text-sm font-medium'>\n <input\n type='checkbox'\n checked={value.wantsFullInvoice}\n onChange={(e) => set('wantsFullInvoice', e.target.checked)}\n />\n {t.toggle}\n </label>\n <p className='text-sm text-muted-foreground'>{t.underCapNotice}</p>\n </div>\n )}\n\n {showFields && (\n <div className='grid grid-cols-1 gap-4 sm:grid-cols-2'>\n <div className='space-y-1'>\n <label htmlFor='full-invoice-id-type' className='text-sm font-medium'>\n {t.idType}\n </label>\n <select\n id='full-invoice-id-type'\n className={FIELD_CLASS}\n required\n value={value.billingIdType}\n onChange={(e) => set('billingIdType', e.target.value as FullInvoiceIdType)}\n {...invalid('billingIdType')}\n >\n {ID_TYPES.map((type) => (\n <option key={type} value={type}>\n {t.idTypeOptions[type]}\n </option>\n ))}\n </select>\n {error('billingIdType')}\n </div>\n\n {!isSpanishNif && (\n <div className='space-y-1'>\n <label htmlFor='full-invoice-id-country' className='text-sm font-medium'>\n {t.idCountry}\n </label>\n <select\n id='full-invoice-id-country'\n className={FIELD_CLASS}\n required\n value={value.billingIdCountry}\n onChange={(e) => set('billingIdCountry', e.target.value)}\n {...invalid('billingIdCountry')}\n >\n <option value='' disabled>\n {t.chooseCountry}\n </option>\n {countries.map((c) => (\n <option key={c.code} value={c.code}>\n {c.name}\n </option>\n ))}\n </select>\n {error('billingIdCountry')}\n </div>\n )}\n\n <div className='space-y-1'>\n <label htmlFor='full-invoice-nif' className='text-sm font-medium'>\n {isSpanishNif ? t.spanishNif : t.documentNumber}\n </label>\n <input\n id='full-invoice-nif'\n className={FIELD_CLASS}\n required\n value={value.billingNif}\n onChange={(e) => set('billingNif', e.target.value)}\n {...invalid('billingNif')}\n />\n {error('billingNif')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor='full-invoice-name' className='text-sm font-medium'>\n {t.name}\n </label>\n <input\n id='full-invoice-name'\n className={FIELD_CLASS}\n required\n value={value.billingName}\n onChange={(e) => set('billingName', e.target.value)}\n {...invalid('billingName')}\n />\n {error('billingName')}\n </div>\n\n <div className='space-y-1 sm:col-span-2'>\n <label htmlFor='full-invoice-street' className='text-sm font-medium'>\n {t.street}\n </label>\n <input\n id='full-invoice-street'\n className={FIELD_CLASS}\n required\n value={value.street}\n onChange={(e) => set('street', e.target.value)}\n {...invalid('address')}\n />\n </div>\n <div className='space-y-1'>\n <label htmlFor='full-invoice-postal-code' className='text-sm font-medium'>\n {t.postalCode}\n </label>\n <input\n id='full-invoice-postal-code'\n className={FIELD_CLASS}\n required\n value={value.postalCode}\n onChange={(e) => set('postalCode', e.target.value)}\n />\n </div>\n <div className='space-y-1'>\n <label htmlFor='full-invoice-city' className='text-sm font-medium'>\n {t.city}\n </label>\n <input\n id='full-invoice-city'\n className={FIELD_CLASS}\n required\n value={value.city}\n onChange={(e) => set('city', e.target.value)}\n />\n </div>\n <div className='space-y-1'>\n <label htmlFor='full-invoice-country' className='text-sm font-medium'>\n {t.country}\n </label>\n <select\n id='full-invoice-country'\n className={FIELD_CLASS}\n required\n value={value.country}\n onChange={(e) => set('country', e.target.value)}\n >\n {countries.map((c) => (\n <option key={c.code} value={c.code}>\n {c.name}\n </option>\n ))}\n </select>\n </div>\n <div className='sm:col-span-2'>{error('address')}</div>\n </div>\n )}\n </section>\n )\n}\n"],"mappings":"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","names":["import_utils","import_jsx_runtime","DEFAULT_LABELS","import_react","import_utils","import_jsx_runtime","DEFAULT_LABELS","import_react","import_utils","import_jsx_runtime","DEFAULT_LABELS","count","import_react","import_utils","import_jsx_runtime","import_utils","import_jsx_runtime","import_jsx_runtime","import_react","import_utils","import_utils","import_jsx_runtime","module","import_react","import_utils","import_jsx_runtime","import_react","import_utils","import_jsx_runtime","FIELD_ORDER","EMAIL_PATTERN","postToRoute","rejectionMessage","FIELD_CLASS","import_react","import_utils","import_utils","import_jsx_runtime","import_utils","import_jsx_runtime","import_utils","import_jsx_runtime","DEFAULT_LABELS","FIELD_CLASS","DEFAULT_LABELS","import_jsx_runtime","EMAIL_PATTERN","FIELD_CLASS","postToRoute"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/hooks/use-collection.ts","../src/components/collection.tsx","../src/components/share-buttons.tsx","../src/lib/share-urls.ts","../src/components/add-to-cart-button.tsx","../src/components/cart-icon.tsx","../src/components/product-carousel.tsx","../src/components/_heading.tsx","../src/components/product-card.tsx","../src/lib/format-price.ts","../src/components/course-syllabus.tsx","../src/lib/slot-class-name.ts","../src/components/data-request-form.tsx","../src/components/withdrawal-request-form.tsx","../src/components/course-guest-checkout-form.tsx","../src/lib/course-guest-checkout.ts","../src/components/checkout-pay-action.tsx","../src/components/checkout-terms-acceptance.tsx","../src/components/full-invoice-fields.tsx","../src/components/reviews-carousel.tsx","../src/components/_review-card.tsx","../src/components/reviews.tsx","../src/components/featured-reviews.tsx","../src/components/reviews-block-renderer.tsx","../src/components/team-directory.tsx","../src/lib/page-links.ts","../src/components/profile-card.tsx","../src/components/professional-fields.tsx"],"sourcesContent":["'use client'\n\n// `@reblu/site-blocks/client` — the client-only surface. Every export here needs\n// browser state or hooks, so the entry carries the `'use client'` directive and\n// each SITE consumes it from a Client Component boundary.\n\nexport {\n useCollection,\n type CollectionFilter,\n type UseCollectionOptions,\n type UseCollectionResult,\n} from './hooks/use-collection'\n\nexport { Collection } from './components/collection'\nexport type {\n CollectionProps,\n CollectionFacet,\n CollectionFacetOption,\n CollectionLabels,\n} from './components/collection'\n\nexport { ShareButtons } from './components/share-buttons'\nexport type { ShareButtonsProps, ShareButtonsLabels } from './components/share-buttons'\n\n// ── Cart feedback (REB-837) ──────────────────────────────────────────────────\n// `AddToCartButton`/`CartIcon` moved here from the RSC-safe root: both now\n// carry real internal state (confirmed/error timer, bump-on-add guard).\nexport {\n AddToCartButton,\n type AddToCartButtonProps,\n type AddToCartButtonLabels,\n type AddToCartButtonStatus,\n} from './components/add-to-cart-button'\nexport { CartIcon, type CartIconProps, type CartIconLabels } from './components/cart-icon'\n\n// ── Product carousel (REB-1120) ──────────────────────────────────────────────\n// Scroll-snap strip with prev/next arrows for «Productos relacionados»; it\n// tracks the scroll position, so it is client-only.\nexport {\n ProductCarousel,\n DEFAULT_PRODUCT_CAROUSEL_LABELS,\n type ProductCarouselProps,\n type ProductCarouselDefaultCardProps,\n type ProductCarouselCustomCardProps,\n type ProductCarouselLabels,\n} from './components/product-carousel'\n\n// ── Course programme (REB-1134) ──────────────────────────────────────────────\n// Expandable module → lesson list over `AcademyCourseDetail.modules`; it holds\n// each module's open/closed state, so it is client-only.\nexport {\n CourseSyllabus,\n DEFAULT_COURSE_SYLLABUS_LABELS,\n type CourseSyllabusProps,\n type CourseSyllabusModule,\n type CourseSyllabusLabels,\n} from './components/course-syllabus'\n\n// ── GDPR rights form (REB-980) ───────────────────────────────────────────────\n// Drop-in public form over `POST /api/site/data-requests` (REB-77).\nexport {\n DataRequestForm,\n DATA_REQUEST_TYPES,\n DATA_REQUEST_FIELD_LIMITS,\n DEFAULT_DATA_REQUEST_FORM_LABELS,\n type DataRequestFormProps,\n type DataRequestFormLabels,\n type DataRequestSubmitResult,\n} from './components/data-request-form'\n\n// ── Withdrawal form (REB-1201) ───────────────────────────────────────────────\n// Drop-in form over `POST /api/site/withdrawal-requests`.\nexport {\n WithdrawalRequestForm,\n WITHDRAWAL_REQUEST_FIELD_LIMITS,\n DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS,\n type WithdrawalRequestFormProps,\n type WithdrawalRequestFormLabels,\n type WithdrawalRequestSubmitResult,\n} from './components/withdrawal-request-form'\n\n// ── Guest course checkout (REB-1264) ─────────────────────────────────────────\n// Drop-in one-step checkout over `POST /api/site/academy/checkout` for a\n// course that needs no validation; gives way to the request form otherwise.\nexport {\n CourseGuestCheckoutForm,\n DEFAULT_COURSE_GUEST_CHECKOUT_FORM_LABELS,\n DEFAULT_COURSE_GUEST_CHECKOUT_ERROR_MESSAGES,\n type CourseGuestCheckoutFormProps,\n type CourseGuestCheckoutFormLabels,\n type CourseGuestCheckoutCopy,\n type CourseGuestCheckoutInput,\n type CourseGuestCheckoutResult,\n} from './components/course-guest-checkout-form'\n\n// REB-1272 — the tenant's published reviews as an accessible carousel (no autoplay).\nexport {\n ReviewsCarousel,\n DEFAULT_REVIEWS_CAROUSEL_LABELS,\n type ReviewsCarouselProps,\n type ReviewsCarouselLabels,\n} from './components/reviews-carousel'\n\n// REB-1272 (owner, 8-Nov) — the reviews featured on the home, in the layout\n// the tenant chose in nexus (grid or carousel).\nexport {\n FeaturedReviews,\n type FeaturedReviewsProps,\n type FeaturedReviewsLayout,\n} from './components/featured-reviews'\n\n// REB-1272 — the «Reseñas» page block body with the carousel layout, for\n// `<PageBlocks components={{ reviews: ReviewsBlockRenderer }} />`.\nexport { ReviewsBlockRenderer } from './components/reviews-block-renderer'\n\n// ── Team directory (REB-1100) ────────────────────────────────────────────────\n// The team listing with one filter chip per linked page; the selected chip is\n// client state.\nexport {\n TeamDirectory,\n DEFAULT_TEAM_DIRECTORY_LABELS,\n type TeamDirectoryProps,\n type TeamDirectoryMember,\n type TeamDirectoryLabels,\n} from './components/team-directory'\n","'use client'\n\nimport { useCallback, useMemo, useState } from 'react'\n\n/**\n * `useCollection` — the headless heart of `@reblu/site-blocks`.\n *\n * A framework-agnostic, presentation-free hook that turns an in-memory array into\n * a filtered / searched / sorted / paginated view. It backs BOTH the page index\n * and the blog index: the facets differ (category, tag, …), the machinery does\n * not. Callers own how items look (`Collection` or their own markup); this hook\n * only owns *which* items and in *what* order.\n *\n * All derivation is memoised and pure — comparators run over `toSorted`, never\n * mutating the caller's array.\n */\n\n/** A named facet: `key` identifies it, `predicate` decides membership for a value. */\nexport interface CollectionFilter<T> {\n key: string\n predicate: (item: T, value: string) => boolean\n}\n\nexport interface UseCollectionOptions<T> {\n items: T[]\n /** Page size. `0`/omitted → no pagination (one page with everything). */\n pageSize?: number\n initialPage?: number\n filters?: CollectionFilter<T>[]\n initialFilters?: Record<string, string | undefined>\n /** Free-text matcher; only applied when a non-empty term is set. */\n search?: (item: T, term: string) => boolean\n initialSearch?: string\n /** Named comparators, selected by `sort`. */\n sorts?: Record<string, (a: T, b: T) => number>\n initialSort?: string\n}\n\nexport interface UseCollectionResult<T> {\n /** Items on the current page (or all items when pagination is off). */\n items: T[]\n /** Full filtered + sorted set, before pagination. */\n filtered: T[]\n /** Size of `filtered`. */\n total: number\n page: number\n pageCount: number\n pageSize: number\n isEmpty: boolean\n activeFilters: Record<string, string | undefined>\n setFilter: (key: string, value: string | undefined) => void\n clearFilters: () => void\n search: string\n setSearch: (term: string) => void\n sort: string | undefined\n setSort: (key: string | undefined) => void\n setPage: (page: number) => void\n nextPage: () => void\n prevPage: () => void\n}\n\nexport function useCollection<T>(options: UseCollectionOptions<T>): UseCollectionResult<T> {\n const { items, pageSize = 0, filters = [], search: searchFn, sorts = {} } = options\n\n const [activeFilters, setActiveFilters] = useState<Record<string, string | undefined>>(\n () => options.initialFilters ?? {},\n )\n const [search, setSearchState] = useState(() => options.initialSearch ?? '')\n const [sort, setSortState] = useState<string | undefined>(() => options.initialSort)\n const [page, setPageState] = useState(() => options.initialPage ?? 1)\n\n const filtered = useMemo(() => {\n let next = items\n\n for (const { key, predicate } of filters) {\n const value = activeFilters[key]\n if (value != null && value !== '') {\n next = next.filter((item) => predicate(item, value))\n }\n }\n\n const term = search.trim()\n if (searchFn && term) {\n next = next.filter((item) => searchFn(item, term))\n }\n\n const comparator = sort ? sorts[sort] : undefined\n if (comparator) {\n next = next.slice().sort(comparator)\n }\n\n return next\n }, [items, filters, activeFilters, search, searchFn, sort, sorts])\n\n const total = filtered.length\n const pageCount = pageSize > 0 ? Math.max(1, Math.ceil(total / pageSize)) : 1\n // Derive the effective page so a shrinking result set never strands us past the\n // last page — state stays as-is, but what we render is always clamped.\n const currentPage = Math.min(Math.max(1, page), pageCount)\n\n const pageItems = useMemo(() => {\n if (pageSize <= 0) return filtered\n const start = (currentPage - 1) * pageSize\n return filtered.slice(start, start + pageSize)\n }, [filtered, pageSize, currentPage])\n\n // Any change to what we're viewing snaps back to the first page.\n const resetToFirstPage = useCallback(() => setPageState(1), [])\n\n const setFilter = useCallback(\n (key: string, value: string | undefined) => {\n setActiveFilters((prev) => ({ ...prev, [key]: value }))\n resetToFirstPage()\n },\n [resetToFirstPage],\n )\n\n const clearFilters = useCallback(() => {\n setActiveFilters({})\n resetToFirstPage()\n }, [resetToFirstPage])\n\n const setSearch = useCallback(\n (term: string) => {\n setSearchState(term)\n resetToFirstPage()\n },\n [resetToFirstPage],\n )\n\n const setSort = useCallback(\n (key: string | undefined) => {\n setSortState(key)\n resetToFirstPage()\n },\n [resetToFirstPage],\n )\n\n // Navigation clamps against the live page bounds so a shrinking result set or\n // an over-shooting jump can never strand the state past the last page.\n const setPage = useCallback(\n (next: number) => setPageState(Math.min(Math.max(1, next), pageCount)),\n [pageCount],\n )\n const nextPage = useCallback(\n () => setPageState(Math.min(currentPage + 1, pageCount)),\n [currentPage, pageCount],\n )\n const prevPage = useCallback(() => setPageState(Math.max(1, currentPage - 1)), [currentPage])\n\n return {\n items: pageItems,\n filtered,\n total,\n page: currentPage,\n pageCount,\n pageSize,\n isEmpty: total === 0,\n activeFilters,\n setFilter,\n clearFilters,\n search,\n setSearch,\n sort,\n setSort,\n setPage,\n nextPage,\n prevPage,\n }\n}\n","'use client'\n\nimport * as React from 'react'\nimport { cn } from '@reblu/site-ui/utils'\n\nimport { useCollection, type CollectionFilter } from '../hooks/use-collection'\n\n/**\n * `Collection` — token-driven presentational grid over {@link useCollection}.\n *\n * The headless hook owns filtering/sorting/pagination; this component owns the\n * chrome: a facet chip bar, the item grid (via a `renderItem` prop), and simple\n * pagination. It carries no brand aesthetic — colors come from tokens; a facet\n * option may carry a `color` (tenant hex) which is applied through a CSS custom\n * property, never baked into a class name.\n *\n * Serves both the page index and the blog index; only the facets differ.\n */\nexport interface CollectionFacetOption {\n value: string\n label: string\n /** Optional tenant color (hex). Applied via a CSS var, not a hardcoded class. */\n color?: string\n}\n\nexport interface CollectionFacet {\n key: string\n label: string\n options: CollectionFacetOption[]\n /** Label for the \"clear this facet\" chip. Defaults to \"All\". */\n allLabel?: string\n}\n\nexport interface CollectionLabels {\n previous: string\n next: string\n page: (page: number, pageCount: number) => string\n}\n\nconst DEFAULT_LABELS: CollectionLabels = {\n previous: 'Previous',\n next: 'Next',\n page: (page, pageCount) => `Page ${page} of ${pageCount}`,\n}\n\nexport interface CollectionProps<T> {\n items: T[]\n renderItem: (item: T) => React.ReactNode\n getKey: (item: T) => string\n filters?: CollectionFilter<T>[]\n facets?: CollectionFacet[]\n pageSize?: number\n sorts?: Record<string, (a: T, b: T) => number>\n initialFilters?: Record<string, string | undefined>\n initialSort?: string\n emptyLabel?: string\n className?: string\n /** Tailwind classes for the grid container. */\n gridClassName?: string\n labels?: Partial<CollectionLabels>\n}\n\nexport function Collection<T>({\n items,\n renderItem,\n getKey,\n filters,\n facets = [],\n pageSize = 0,\n sorts,\n initialFilters,\n initialSort,\n emptyLabel = 'No results',\n className,\n gridClassName = 'grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3',\n labels,\n}: CollectionProps<T>) {\n const t = { ...DEFAULT_LABELS, ...labels }\n const collection = useCollection<T>({\n items,\n filters,\n pageSize,\n sorts,\n initialFilters,\n initialSort,\n })\n\n return (\n <div className={cn('space-y-8', className)}>\n {facets.length > 0 && (\n <div className='space-y-4'>\n {facets.map((facet) => {\n const active = collection.activeFilters[facet.key]\n return (\n <div\n key={facet.key}\n role='group'\n aria-label={facet.label}\n className='flex flex-wrap gap-2'\n >\n <FacetChip\n label={facet.allLabel ?? 'All'}\n pressed={active == null || active === ''}\n onClick={() => collection.setFilter(facet.key, undefined)}\n />\n {facet.options.map((opt) => (\n <FacetChip\n key={opt.value}\n label={opt.label}\n color={opt.color}\n pressed={active === opt.value}\n onClick={() => collection.setFilter(facet.key, opt.value)}\n />\n ))}\n </div>\n )\n })}\n </div>\n )}\n\n {collection.isEmpty ? (\n <p className='py-16 text-center text-muted-foreground'>{emptyLabel}</p>\n ) : (\n <ul className={cn('list-none p-0', gridClassName)}>\n {collection.items.map((item) => (\n <li key={getKey(item)}>{renderItem(item)}</li>\n ))}\n </ul>\n )}\n\n {collection.pageCount > 1 && (\n <nav aria-label='Pagination' className='flex items-center justify-center gap-4'>\n <button\n type='button'\n onClick={collection.prevPage}\n disabled={collection.page <= 1}\n className={PAGE_BTN_CLASS}\n >\n {t.previous}\n </button>\n <span aria-current='page' className='text-sm text-muted-foreground'>\n {t.page(collection.page, collection.pageCount)}\n </span>\n <button\n type='button'\n onClick={collection.nextPage}\n disabled={collection.page >= collection.pageCount}\n className={PAGE_BTN_CLASS}\n >\n {t.next}\n </button>\n </nav>\n )}\n </div>\n )\n}\n\nconst PAGE_BTN_CLASS =\n 'inline-flex h-9 cursor-pointer items-center rounded-md border border-border px-4 text-sm font-medium transition-colors hover:bg-muted disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'\n\ninterface FacetChipProps {\n label: string\n pressed: boolean\n color?: string\n onClick: () => void\n}\n\nfunction FacetChip({ label, pressed, color, onClick }: FacetChipProps) {\n return (\n <button\n type='button'\n aria-pressed={pressed}\n onClick={onClick}\n // The tenant color rides in a CSS var; the class picks it up when pressed,\n // so no hex string is ever baked into a Tailwind class.\n style={color ? ({ '--facet-color': color } as React.CSSProperties) : undefined}\n className={cn(\n 'inline-flex cursor-pointer items-center rounded-full border px-3 py-1 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n pressed\n ? color\n ? 'border-transparent bg-[var(--facet-color)] text-white'\n : 'border-transparent bg-primary text-primary-foreground'\n : 'border-border text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n {label}\n </button>\n )\n}\n","'use client'\n\nimport * as React from 'react'\nimport { cn } from '@reblu/site-ui/utils'\n\nimport { buildShareUrls } from '../lib/share-urls'\n\n/**\n * `ShareButtons` — social share row for a blog post (LinkedIn / X / Facebook /\n * WhatsApp) plus a copy-to-clipboard action. Token-driven and icon-only for the\n * providers; the copy action shows a text confirmation.\n *\n * Copy defaults to English (distributable default). A Spanish-or-other SITE can\n * override every visible string via `labels` without forking the component.\n */\nexport interface ShareButtonsLabels {\n share: string\n copy: string\n copied: string\n linkedin: string\n x: string\n facebook: string\n whatsapp: string\n copyStatus: string\n}\n\nconst DEFAULT_LABELS: ShareButtonsLabels = {\n share: 'Share',\n copy: 'Copy link',\n copied: 'Link copied',\n linkedin: 'Share on LinkedIn',\n x: 'Share on X',\n facebook: 'Share on Facebook',\n whatsapp: 'Share on WhatsApp',\n copyStatus: 'Link copied to clipboard',\n}\n\nexport interface ShareButtonsProps {\n /** Canonical, absolute URL of the post being shared. */\n url: string\n /** Post title, used where the provider supports pre-filled copy. */\n title: string\n className?: string\n labels?: Partial<ShareButtonsLabels>\n}\n\nconst LINK_CLASS =\n 'inline-flex h-9 w-9 items-center justify-center rounded-full border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'\n\nconst ICON = 'h-4 w-4'\n\nfunction LinkedInIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='currentColor' aria-hidden='true' focusable='false'>\n <path d='M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zM8.34 18.34V10.9H5.9v7.44zM7.12 9.82a1.42 1.42 0 1 0 0-2.84 1.42 1.42 0 0 0 0 2.84m11.22 8.52v-4.08c0-2.18-1.16-3.19-2.72-3.19a2.35 2.35 0 0 0-2.13 1.17v-1h-2.43v7.1h2.43v-3.93c0-1 .2-2 1.46-2s1.28 1.16 1.28 2.06v3.87z' />\n </svg>\n )\n}\n\nfunction XIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='currentColor' aria-hidden='true' focusable='false'>\n <path d='M18.244 2.25h3.308l-7.227 8.26 8.502 11.24h-6.66l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z' />\n </svg>\n )\n}\n\nfunction FacebookIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='currentColor' aria-hidden='true' focusable='false'>\n <path d='M22 12a10 10 0 1 0-11.56 9.88v-6.99H7.9V12h2.54V9.8c0-2.5 1.49-3.89 3.78-3.89 1.09 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56V12h2.78l-.44 2.89h-2.34v6.99A10 10 0 0 0 22 12' />\n </svg>\n )\n}\n\nfunction WhatsAppIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='currentColor' aria-hidden='true' focusable='false'>\n <path d='M12.04 2C6.58 2 2.13 6.45 2.13 11.91c0 1.75.46 3.45 1.32 4.95L2 22l5.25-1.38a9.9 9.9 0 0 0 4.79 1.22h.01c5.46 0 9.91-4.45 9.91-9.91 0-2.65-1.03-5.14-2.9-7.01A9.82 9.82 0 0 0 12.04 2m0 1.67c2.2 0 4.27.86 5.82 2.42a8.19 8.19 0 0 1 2.42 5.82c0 4.54-3.7 8.24-8.25 8.24a8.2 8.2 0 0 1-4.2-1.15l-.3-.18-3.12.82.83-3.04-.2-.31a8.2 8.2 0 0 1-1.26-4.38c0-4.54 3.7-8.24 8.25-8.24m4.52 10.36c-.06-.1-.22-.16-.46-.28s-1.46-.72-1.68-.8-.39-.12-.55.12-.63.8-.77.96-.28.18-.52.06a6.7 6.7 0 0 1-1.98-1.22 7.4 7.4 0 0 1-1.37-1.7c-.14-.24-.01-.37.11-.49.11-.11.24-.28.37-.42.12-.14.16-.24.24-.4s.04-.3-.02-.42-.55-1.32-.75-1.81c-.2-.48-.4-.41-.55-.42h-.47a.9.9 0 0 0-.65.3c-.22.24-.85.83-.85 2.02s.87 2.35 1 2.51c.12.16 1.71 2.61 4.14 3.66.58.25 1.03.4 1.38.51.58.19 1.11.16 1.53.1.47-.07 1.46-.6 1.66-1.17.21-.58.21-1.07.15-1.17' />\n </svg>\n )\n}\n\nfunction CopyIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='none' stroke='currentColor' strokeWidth='2' strokeLinecap='round' strokeLinejoin='round' aria-hidden='true' focusable='false'>\n <rect x='9' y='9' width='13' height='13' rx='2' ry='2' />\n <path d='M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1' />\n </svg>\n )\n}\n\nfunction CheckIcon() {\n return (\n <svg className={ICON} viewBox='0 0 24 24' fill='none' stroke='currentColor' strokeWidth='2' strokeLinecap='round' strokeLinejoin='round' aria-hidden='true' focusable='false'>\n <polyline points='20 6 9 17 4 12' />\n </svg>\n )\n}\n\nexport function ShareButtons({ url, title, className, labels }: ShareButtonsProps) {\n const t = { ...DEFAULT_LABELS, ...labels }\n const [copied, setCopied] = React.useState(false)\n const share = buildShareUrls(url, title)\n\n async function handleCopy() {\n try {\n await navigator.clipboard.writeText(url)\n setCopied(true)\n window.setTimeout(() => setCopied(false), 2000)\n } catch {\n // Clipboard can be unavailable (permissions, insecure context); fail quietly.\n }\n }\n\n return (\n <div className={cn('flex items-center gap-2', className)} role='group' aria-label={t.share}>\n <span className='mr-1 text-sm font-medium text-muted-foreground'>{t.share}</span>\n\n <a href={share.linkedin} target='_blank' rel='noopener noreferrer' className={LINK_CLASS} aria-label={t.linkedin}>\n <LinkedInIcon />\n </a>\n <a href={share.x} target='_blank' rel='noopener noreferrer' className={LINK_CLASS} aria-label={t.x}>\n <XIcon />\n </a>\n <a href={share.facebook} target='_blank' rel='noopener noreferrer' className={LINK_CLASS} aria-label={t.facebook}>\n <FacebookIcon />\n </a>\n <a href={share.whatsapp} target='_blank' rel='noopener noreferrer' className={LINK_CLASS} aria-label={t.whatsapp}>\n <WhatsAppIcon />\n </a>\n\n <button\n type='button'\n onClick={handleCopy}\n className={cn(LINK_CLASS, 'w-auto cursor-pointer gap-2 px-3', copied && 'border-primary text-primary')}\n aria-label={t.copy}\n >\n {copied ? <CheckIcon /> : <CopyIcon />}\n <span className='text-sm'>{copied ? t.copied : t.copy}</span>\n </button>\n\n {/* Polite live region so screen readers announce the copy confirmation. */}\n <span role='status' aria-live='polite' className='sr-only'>\n {copied ? t.copyStatus : ''}\n </span>\n </div>\n )\n}\n","/**\n * Pure builders for the social-share targets used by `ShareButtons`.\n *\n * Framework-free so the URL construction is unit-testable without rendering.\n * Every provider gets the canonical post URL; providers that support custom copy\n * (X, WhatsApp) also receive the post title.\n */\nexport interface ShareUrls {\n linkedin: string\n x: string\n facebook: string\n whatsapp: string\n}\n\nexport function buildShareUrls(url: string, title: string): ShareUrls {\n const encodedUrl = encodeURIComponent(url)\n const encodedTitle = encodeURIComponent(title)\n\n return {\n // LinkedIn derives title/description from the target page's Open Graph tags,\n // so it only takes the URL.\n linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,\n // The intent endpoint keeps the /intent/tweet path under the x.com domain.\n x: `https://x.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`,\n facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,\n whatsapp: `https://wa.me/?text=${encodeURIComponent(`${title} ${url}`)}`,\n }\n}\n","'use client'\n\nimport { useEffect, useRef, useState } from 'react'\n\nimport { cn } from '@reblu/site-ui/utils'\n\n/**\n * `AddToCartButton` — add-to-cart CTA with a built-in confirmed/error state\n * (REB-837). Moved to the `./client` entry (from the RSC-safe root) because\n * the confirmed-state timer and the `error` prop watcher genuinely need\n * `useState`/`useEffect` — see `client.ts`'s own leading `'use client'`\n * directive (R4: tsup only preserves a directive declared by the entry file\n * itself, not one merely re-exported from a child module).\n *\n * Previously (REB-829) this component had no internal state at all — it was\n * a pure `onAddToCart?: () => void` button exported from the RSC-safe root.\n * A SITE importing `AddToCartButton` from `@reblu/site-blocks` (the root\n * entry) must now import it from `@reblu/site-blocks/client` instead.\n */\nexport interface AddToCartButtonLabels {\n add: string\n adding: string\n outOfStock: string\n /** Shown for ~`successDurationMs` after a successful add. */\n added: string\n /** Fallback shown when `error` is `true` (not a string). */\n error: string\n}\n\nconst DEFAULT_LABELS: AddToCartButtonLabels = {\n add: 'Añadir al carrito',\n adding: 'Añadiendo…',\n outOfStock: 'Agotado',\n added: 'Añadido ✓',\n error: 'No se pudo añadir',\n}\n\nexport type AddToCartButtonStatus = 'idle' | 'success' | 'error'\n\n/**\n * Structural subset of `@reblu/site-next/cart`'s `AddItemOutcome` (REB-837\n * review fix) — `site-blocks` never depends on `site-next`, so this is\n * duplicated shape, not an import; a real `AddItemOutcome` (which also\n * carries `quantity`) satisfies this type as-is.\n */\nexport interface AddToCartOutcome {\n status: 'added' | 'clamped' | 'refused'\n}\n\nexport interface AddToCartButtonProps<TItem = unknown> {\n /**\n * Called on click. MAY return the outcome of the add (synchronously or\n * via a `Promise`) so the button can decide success vs error from the\n * RESULT instead of only from the externally-controlled `error` prop\n * (REB-837 review fix: deciding purely from `error` captured at render\n * time showed \"Añadido ✓\" for a REFUSED add whenever the caller never\n * flips `error` back on). `'refused'` → error state; `'added'`/`'clamped'`\n * → confirmed state; returning nothing (`void`) stays backward compatible\n * with the original fire-and-forget contract — treated as success.\n */\n onAddToCart?: (item?: TItem) => AddToCartOutcome | void | Promise<AddToCartOutcome | void>\n disabled?: boolean\n outOfStock?: boolean\n /** True while the add-to-cart call (or a navigation it triggers) is in flight. */\n pending?: boolean\n /**\n * Controlled error (out-of-stock race, max-quantity reached, a failed\n * mutation…). Set it truthy right after `onAddToCart` fails to trigger the\n * error state; clear it (`undefined`/`false`) to let the button reset. A\n * `string` is shown as the button's label; `true` falls back to\n * `labels.error`.\n */\n error?: string | boolean\n /** Opaque item forwarded verbatim to `onAddToCart`/`onAdded`/`onError` — never read internally. */\n item?: TItem\n /** How long the success/error state shows before reverting to idle, ms. @default 1500 */\n successDurationMs?: number\n /** Fires once per successful add, right after `onAddToCart`. */\n onAdded?: (item?: TItem) => void\n /** Fires once when `error` transitions from falsy to truthy. */\n onError?: (item?: TItem, error?: string | boolean) => void\n labels?: Partial<AddToCartButtonLabels>\n className?: string\n}\n\nexport function AddToCartButton<TItem = unknown>({\n onAddToCart,\n disabled,\n outOfStock,\n pending,\n error,\n item,\n successDurationMs = 1500,\n onAdded,\n onError,\n labels,\n className,\n}: AddToCartButtonProps<TItem>) {\n const t = { ...DEFAULT_LABELS, ...labels }\n const [status, setStatus] = useState<AddToCartButtonStatus>('idle')\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const prevErrorRef = useRef<string | boolean | undefined>(error)\n // Non-reentrancy guard (REB-837): `disabled`/`aria-disabled` already block\n // both pointer and keyboard activation of a native <button>, but a ref\n // guard is the actual source of truth for \"is a success window open OR an\n // async onAddToCart is still resolving\" — defense in depth against\n // anything that might still dispatch a click, and the only thing that\n // actually blocks a double-submit while a Promise is pending.\n const inSuccessWindowRef = useRef(false)\n const resolvingRef = useRef(false)\n const mountedRef = useRef(true)\n // Mirrors `resolvingRef` into render state so the button is ALSO visibly\n // disabled (not just logically blocked) while an async `onAddToCart` is\n // still pending — `resolvingRef` alone is enough to stop a double-submit,\n // but a sighted user should see the button go inert too.\n const [resolving, setResolving] = useState(false)\n\n const clearTimer = () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n timeoutRef.current = null\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n return () => {\n mountedRef.current = false\n clearTimer()\n }\n }, [])\n\n const triggerSuccess = () => {\n inSuccessWindowRef.current = true\n clearTimer()\n setStatus('success')\n onAdded?.(item)\n timeoutRef.current = setTimeout(() => {\n inSuccessWindowRef.current = false\n setStatus('idle')\n }, successDurationMs)\n }\n\n const triggerError = (errorValue: string | boolean = true) => {\n clearTimer()\n inSuccessWindowRef.current = false\n setStatus('error')\n onError?.(item, errorValue)\n timeoutRef.current = setTimeout(() => setStatus('idle'), successDurationMs)\n }\n\n useEffect(() => {\n if (error && !prevErrorRef.current) {\n triggerError(error)\n }\n prevErrorRef.current = error\n // eslint-disable-next-line react-hooks/exhaustive-deps -- only re-run on `error` transitions\n }, [error])\n\n /**\n * Applies the RESULT of `onAddToCart` (REB-837 review fix): a `'refused'`\n * outcome always triggers the error state, regardless of the `error` prop\n * — the whole point is that the caller may not control that prop at all\n * (e.g. it derives the outcome from `addItem()`'s own return value, never\n * touching `error`). Anything else (`'added'`, `'clamped'`, or no return\n * value at all — the original fire-and-forget contract) is success,\n * UNLESS the externally-controlled `error` prop is ALSO currently truthy.\n */\n const applyOutcome = (result: AddToCartOutcome | void) => {\n if (!mountedRef.current || outOfStock) return\n if (result?.status === 'refused') {\n triggerError(true)\n return\n }\n if (error) return\n triggerSuccess()\n }\n\n const handleClick = () => {\n if (inSuccessWindowRef.current || resolvingRef.current) return\n const result = onAddToCart?.(item)\n\n if (result && typeof (result as Promise<AddToCartOutcome | void>).then === 'function') {\n resolvingRef.current = true\n setResolving(true)\n ;(result as Promise<AddToCartOutcome | void>)\n .then((resolved) => {\n resolvingRef.current = false\n if (mountedRef.current) setResolving(false)\n applyOutcome(resolved)\n })\n .catch(() => {\n resolvingRef.current = false\n if (mountedRef.current) setResolving(false)\n applyOutcome({ status: 'refused' })\n })\n return\n }\n\n applyOutcome(result as AddToCartOutcome | void)\n }\n\n const isDisabled = Boolean(disabled || outOfStock || pending || status === 'success' || resolving)\n const label = outOfStock\n ? t.outOfStock\n : status === 'success'\n ? t.added\n : status === 'error'\n ? typeof error === 'string'\n ? error\n : t.error\n : pending || resolving\n ? t.adding\n : t.add\n\n return (\n <button\n type='button'\n onClick={handleClick}\n disabled={isDisabled}\n aria-disabled={isDisabled}\n aria-live='polite'\n data-slot='add-to-cart-button'\n data-status={status}\n className={cn(\n 'inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n isDisabled\n ? 'cursor-not-allowed bg-muted text-muted-foreground'\n : 'bg-primary text-primary-foreground hover:bg-primary/90',\n status === 'error' && 'bg-destructive text-destructive-foreground',\n className,\n )}\n >\n {label}\n </button>\n )\n}\n","'use client'\n\nimport { useEffect, useRef, useState } from 'react'\n\nimport { cn } from '@reblu/site-ui/utils'\n\n/**\n * `CartIcon` — cart badge/link, with an opt-in count \"bump\" (REB-837). Moved\n * to the `./client` entry (from the RSC-safe root) because the bump\n * animation needs `useState`/`useEffect` to detect an itemCount increase —\n * see `client.ts`'s own leading `'use client'` directive (R4).\n *\n * The HYDRATION GOTCHA this guards against: `CartProvider` rehydrates\n * AFTER mount, so `itemCount` jumps `0 -> N` on every page load with a\n * non-empty persisted cart. Without a guard, that jump reads exactly like \"N\n * items were just added\" and bumps on every single page load. The fix:\n * `hydrated` (pass `useCart().hydrated` straight through) is compared\n * against its OWN previous value, not just itemCount's — a bump only fires\n * when the cart was ALREADY hydrated on the previous comparison AND\n * itemCount increased since. The simultaneous `false -> true` / `0 -> N`\n * rehydration transition fails that first condition; a real add afterwards\n * (hydrated already `true` on both sides) passes it.\n */\nexport interface CartIconLabels {\n cart: string\n itemsSuffix: (count: number) => string\n}\n\nconst DEFAULT_LABELS: CartIconLabels = {\n cart: 'Carrito',\n itemsSuffix: (count) => `${count} artículo${count === 1 ? '' : 's'}`,\n}\n\n/** How long the bump animation plays, ms. */\nconst BUMP_DURATION_MS = 400\n\nexport interface CartIconProps {\n itemCount: number\n href: string\n /**\n * Pass `useCart().hydrated` through when `bumpOnAdd` is enabled — gates\n * the bump against the rehydration jump. Omitting it ENTIRELY (REB-837\n * review fix — not the same as passing `false`) disables the hydration\n * guard altogether, so the bump fires on every itemCount increase; only\n * reach for that when `itemCount` is computed without any client-side\n * rehydration step (e.g. server-rendered, never changes after mount).\n */\n hydrated?: boolean\n /**\n * Enable the count bump when `itemCount` increases after hydration.\n * Respects `prefers-reduced-motion` (the animation classes are\n * `motion-safe:`-gated). @default false\n */\n bumpOnAdd?: boolean\n labels?: Partial<CartIconLabels>\n className?: string\n}\n\nexport function CartIcon({ itemCount, href, hydrated, bumpOnAdd, labels, className }: CartIconProps) {\n const t = { ...DEFAULT_LABELS, ...labels }\n const ariaLabel = itemCount > 0 ? `${t.cart} (${t.itemsSuffix(itemCount)})` : t.cart\n const displayCount = itemCount > 99 ? '99+' : String(itemCount)\n\n const [bumping, setBumping] = useState(false)\n const prevCountRef = useRef(itemCount)\n const prevHydratedRef = useRef(hydrated)\n\n useEffect(() => {\n // REB-837 review fix: `hydrated` OMITTED entirely (never `=== true` nor\n // `=== false`, i.e. `undefined` on the CURRENT render) means the caller\n // isn't using the hydration guard at all — treat it as always-satisfied\n // rather than permanently failing `prevHydratedRef.current === true`\n // (which stayed `undefined` forever and silently disabled every bump).\n const hydrationGuardSatisfied = hydrated === undefined || prevHydratedRef.current === true\n const shouldBump =\n Boolean(bumpOnAdd) && itemCount > prevCountRef.current && hydrationGuardSatisfied\n\n prevCountRef.current = itemCount\n prevHydratedRef.current = hydrated\n\n if (!shouldBump) {\n // REB-837 review fix: a PREVIOUS bump's own setTimeout gets cancelled\n // by this effect's cleanup (dep change) before this body runs — if\n // this run doesn't start a NEW bump, it must still reset the flag\n // itself, or a bump started just before a quick remove/decrease\n // (within `BUMP_DURATION_MS`) gets stuck on `true` forever.\n setBumping(false)\n return\n }\n\n setBumping(true)\n const id = setTimeout(() => setBumping(false), BUMP_DURATION_MS)\n return () => clearTimeout(id)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally only re-runs when itemCount/hydrated/bumpOnAdd change\n }, [itemCount, hydrated, bumpOnAdd])\n\n return (\n <a\n href={href}\n aria-label={ariaLabel}\n data-slot='cart-icon'\n data-bumping={bumping || undefined}\n className={cn(\n 'relative inline-flex items-center justify-center rounded-lg p-2 text-foreground/80 transition-colors hover:bg-muted/60 hover:text-foreground',\n className,\n )}\n >\n <svg\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth={2}\n strokeLinecap='round'\n strokeLinejoin='round'\n className={cn('h-5 w-5', bumping && 'motion-safe:scale-125 motion-safe:transition-transform motion-safe:duration-300')}\n aria-hidden='true'\n >\n <circle cx='9' cy='21' r='1' />\n <circle cx='20' cy='21' r='1' />\n <path d='M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6' />\n </svg>\n {itemCount > 0 && (\n <span\n aria-hidden='true'\n className={cn(\n 'absolute -top-0.5 -right-0.5 flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-primary px-1 text-[10px] font-bold leading-none tabular-nums text-primary-foreground',\n bumping && 'motion-safe:scale-125 motion-safe:transition-transform motion-safe:duration-300',\n )}\n >\n {displayCount}\n </span>\n )}\n </a>\n )\n}\n","'use client'\n\nimport { useCallback, useEffect, useId, useRef, useState, type ReactNode } from 'react'\n\nimport { buttonVariants } from '@reblu/site-ui/button-variants'\nimport { cn } from '@reblu/site-ui/utils'\nimport type { ShopProductListItem } from '@reblu/site-contracts'\n\nimport { Heading, type HeadingLevel } from './_heading'\nimport { ProductCard } from './product-card'\n\n/**\n * `ProductCarousel` — a horizontal product strip («Productos relacionados»,\n * «También te puede interesar»…) with no carousel dependency.\n *\n * - The track is a native scroll container with CSS scroll-snap, so touch users swipe\n * it and keyboard users scroll it once it has focus (it is a labelled, focusable region).\n * - Previous/next arrows (from `sm` up) scroll by one visible page. They are not rendered\n * when everything fits, and the arrow of the end the track is at is disabled.\n * - Arrow scrolling is instant under `prefers-reduced-motion: reduce`.\n * - Cards: the package's `ProductCard` by default, or the consumer's via `renderProduct`.\n */\nexport interface ProductCarouselLabels {\n /** Accessible name of the track when there is no `heading`. */\n region: string\n /** `aria-roledescription` of the track. */\n roleDescription: string\n previous: string\n next: string\n}\n\nexport const DEFAULT_PRODUCT_CAROUSEL_LABELS: ProductCarouselLabels = {\n region: 'Productos relacionados',\n roleDescription: 'carrusel',\n previous: 'Anterior',\n next: 'Siguiente',\n}\n\ninterface ProductCarouselCommonProps {\n /** Visible heading; when set it also names the track. */\n heading?: string\n /** Default: 2. */\n headingLevel?: HeadingLevel\n /** Replaces the heading's typography (see the className rule in the README). */\n headingClassName?: string\n labels?: Partial<ProductCarouselLabels>\n className?: string\n /** Classes for each slide (`<li>`), e.g. a different card width. */\n itemClassName?: string\n}\n\nexport interface ProductCarouselDefaultCardProps extends ProductCarouselCommonProps {\n products: readonly ShopProductListItem[]\n renderProduct?: undefined\n /** Link target per product (e.g. `(p) => \\`/shop/${p.slug}\\``). Omit for non-linking cards. */\n productHref?: (product: ShopProductListItem) => string | undefined\n currencyCode?: string\n locale?: string\n}\n\nexport interface ProductCarouselCustomCardProps<T extends { id: string | number }>\n extends ProductCarouselCommonProps {\n products: readonly T[]\n /** Renders one slide's card — your own product card. */\n renderProduct: (product: T, index: number) => ReactNode\n productHref?: undefined\n currencyCode?: undefined\n locale?: undefined\n}\n\nexport type ProductCarouselProps<T extends { id: string | number } = ShopProductListItem> =\n | ProductCarouselDefaultCardProps\n | ProductCarouselCustomCardProps<T>\n\n/** Tolerance for sub-pixel scroll positions. */\nconst EDGE_EPSILON = 1\n\nfunction prefersReducedMotion(): boolean {\n return typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n ? window.matchMedia('(prefers-reduced-motion: reduce)').matches\n : false\n}\n\nfunction ChevronIcon({ direction }: { direction: 'left' | 'right' }) {\n return (\n <svg\n aria-hidden='true'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth={2}\n strokeLinecap='round'\n strokeLinejoin='round'\n className='rtl:-scale-x-100'\n >\n <path d={direction === 'left' ? 'm15 18-6-6 6-6' : 'm9 18 6-6-6-6'} />\n </svg>\n )\n}\n\nexport function ProductCarousel<T extends { id: string | number } = ShopProductListItem>(\n props: ProductCarouselProps<T>,\n) {\n const { products, heading, headingLevel = 2, headingClassName, labels, className, itemClassName } = props\n const t = { ...DEFAULT_PRODUCT_CAROUSEL_LABELS, ...labels }\n\n const trackId = useId()\n const headingId = useId()\n const trackRef = useRef<HTMLDivElement>(null)\n const [canPrev, setCanPrev] = useState(false)\n const [canNext, setCanNext] = useState(false)\n\n const update = useCallback(() => {\n const el = trackRef.current\n if (!el) return\n const max = el.scrollWidth - el.clientWidth\n // RTL tracks report a negative scrollLeft; distance from the start is its magnitude.\n const pos = Math.abs(el.scrollLeft)\n setCanPrev(pos > EDGE_EPSILON)\n setCanNext(pos < max - EDGE_EPSILON)\n }, [])\n\n useEffect(() => {\n const el = trackRef.current\n if (!el) return\n update()\n el.addEventListener('scroll', update, { passive: true })\n let observer: ResizeObserver | undefined\n if (typeof ResizeObserver !== 'undefined') {\n observer = new ResizeObserver(update)\n observer.observe(el)\n } else {\n window.addEventListener('resize', update)\n }\n return () => {\n el.removeEventListener('scroll', update)\n if (observer) observer.disconnect()\n else window.removeEventListener('resize', update)\n }\n }, [update, products.length])\n\n const scrollByPage = (direction: -1 | 1) => {\n const el = trackRef.current\n if (!el) return\n const rtl = typeof getComputedStyle === 'function' && getComputedStyle(el).direction === 'rtl'\n el.scrollBy({\n left: direction * el.clientWidth * (rtl ? -1 : 1),\n behavior: prefersReducedMotion() ? 'auto' : 'smooth',\n })\n }\n\n if (products.length === 0) return null\n\n const scrollable = canPrev || canNext\n\n return (\n <section data-slot='product-carousel' className={cn('flex flex-col gap-4', className)}>\n {(heading || scrollable) && (\n <div className='flex items-center justify-between gap-4'>\n {heading ? (\n <div id={headingId}>\n <Heading level={headingLevel} className={headingClassName ?? 'text-xl font-semibold'}>\n {heading}\n </Heading>\n </div>\n ) : (\n <span />\n )}\n {scrollable && (\n <div className='hidden gap-2 sm:flex' data-slot='product-carousel-controls'>\n <button\n type='button'\n className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'rounded-full')}\n aria-label={t.previous}\n aria-controls={trackId}\n disabled={!canPrev}\n onClick={() => scrollByPage(-1)}\n >\n <ChevronIcon direction='left' />\n </button>\n <button\n type='button'\n className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'rounded-full')}\n aria-label={t.next}\n aria-controls={trackId}\n disabled={!canNext}\n onClick={() => scrollByPage(1)}\n >\n <ChevronIcon direction='right' />\n </button>\n </div>\n )}\n </div>\n )}\n\n <div\n ref={trackRef}\n id={trackId}\n role='region'\n aria-roledescription={t.roleDescription}\n {...(heading ? { 'aria-labelledby': headingId } : { 'aria-label': t.region })}\n tabIndex={0}\n data-slot='product-carousel-track'\n className='snap-x snap-mandatory overflow-x-auto overscroll-x-contain scroll-smooth rounded-lg pb-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:scroll-auto'\n >\n <ul role='list' className='m-0 flex list-none gap-4 p-0'>\n {products.map((product, index) => (\n <li\n key={product.id}\n className={cn(\n 'shrink-0 snap-start basis-[calc((100%-1rem)/1.5)] sm:basis-[calc((100%-2rem)/3)] lg:basis-[calc((100%-3rem)/4)]',\n itemClassName,\n )}\n >\n {props.renderProduct ? (\n props.renderProduct(product as T, index)\n ) : (\n <ProductCard\n product={product as ShopProductListItem}\n href={props.productHref?.(product as ShopProductListItem)}\n currencyCode={props.currencyCode}\n locale={props.locale}\n className='h-full'\n />\n )}\n </li>\n ))}\n </ul>\n </div>\n </section>\n )\n}\n","import type { ReactNode } from 'react'\n\n/** A heading level a caller can configure — `h1`…`h6`. */\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6\n\n/** One level deeper, clamped at `h6`. */\nexport function nextHeadingLevel(level: HeadingLevel): HeadingLevel {\n return Math.min(level + 1, 6) as HeadingLevel\n}\n\n/** Internal: renders `<h{level}>`. Server-safe (no hooks). */\nexport function Heading({\n level,\n className,\n children,\n}: {\n level: HeadingLevel\n className?: string\n children: ReactNode\n}) {\n const Tag = `h${level}` as const\n return <Tag className={className}>{children}</Tag>\n}\n","import { cn } from '@reblu/site-ui/utils'\nimport type { ShopProductListItem } from '@reblu/site-contracts'\n\nimport { formatPrice } from '../lib/format-price'\n\n/**\n * `ProductCard` — presentational shop product tile over `ShopProductListItem`.\n *\n * Token-driven, RSC-safe (no hooks). Prices arrive as integer cents and are\n * formatted with {@link formatPrice}. A `comparePrice` higher than `price` renders\n * as a struck-through \"was\" amount.\n */\nexport interface ProductCardProps {\n product: ShopProductListItem\n /** Link target for the product (e.g. `/shop/{slug}`). Omit to render non-linking. */\n href?: string\n currencyCode?: string\n locale?: string\n className?: string\n}\n\nexport function ProductCard({ product, href, currencyCode = 'EUR', locale, className }: ProductCardProps) {\n const image = product.images.find((i) => i.isPrimary) ?? product.images[0]\n const hasDiscount = product.comparePrice != null && product.comparePrice > product.price\n\n return (\n <article\n className={cn(\n 'group flex flex-col overflow-hidden rounded-lg border bg-card transition-shadow hover:shadow-lg',\n className,\n )}\n data-slot='product-card'\n >\n <div className='relative aspect-square overflow-hidden bg-muted'>\n {image ? (\n <img\n src={image.url}\n alt={image.alt ?? product.name}\n loading='lazy'\n className='h-full w-full object-cover transition-transform group-hover:scale-105'\n />\n ) : (\n <div className='flex h-full items-center justify-center text-4xl text-muted-foreground/30' aria-hidden='true'>\n 🛍️\n </div>\n )}\n </div>\n\n <div className='flex flex-1 flex-col gap-2 p-4'>\n {product.category && (\n <span className='inline-flex w-fit items-center rounded-full bg-secondary px-2.5 py-0.5 text-xs font-medium text-secondary-foreground'>\n {product.category.name}\n </span>\n )}\n\n <h3 className='text-base font-semibold leading-tight'>\n {href ? (\n <a href={href} className='transition-colors hover:text-primary'>\n {product.name}\n </a>\n ) : (\n product.name\n )}\n </h3>\n\n {product.shortDescription && (\n <p className='line-clamp-2 text-sm text-muted-foreground'>{product.shortDescription}</p>\n )}\n\n <div className='mt-auto flex items-baseline gap-2 pt-2'>\n <span className='text-lg font-bold text-foreground'>\n {formatPrice(product.price, currencyCode, locale)}\n </span>\n {hasDiscount && (\n <span className='text-sm text-muted-foreground line-through'>\n {formatPrice(product.comparePrice as number, currencyCode, locale)}\n </span>\n )}\n </div>\n </div>\n </article>\n )\n}\n","/**\n * Format an integer-cents amount as a localized currency string.\n *\n * All Reblu shop monetary values travel as integer cents (Prisma `Int`, never\n * Decimal), so the SITE never sees floats. This helper is the single place that\n * converts cents → major units and applies `Intl.NumberFormat`, keeping price\n * rendering consistent across `ProductCard` and any future shop organism.\n */\nexport function formatPrice(cents: number, currency = 'EUR', locale = 'es-ES'): string {\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n }).format(cents / 100)\n}\n","'use client'\n\nimport { useId, useState } from 'react'\n\nimport { cn } from '@reblu/site-ui/utils'\nimport type { AcademyLessonPublic, AcademyModulePublic } from '@reblu/site-contracts'\n\nimport type { HeadingLevel } from './_heading'\nimport { slotClassName } from '../lib/slot-class-name'\n\n/**\n * `CourseSyllabus` — the drop-in programme of a public course page, over\n * `AcademyCourseDetail.modules` (`client.academy.bySlug()`), REB-1134; redesigned\n * in REB-1135.\n *\n * - The header reads the heading, then «N módulos · M lecciones · total», and\n * carries an «Expandir todo / Contraer todo» control.\n * - Each module with lessons is a `<button>` with `aria-expanded` and\n * `aria-controls`, showing its lesson count and, when every lesson has one,\n * the module's total duration, with a chevron that rotates when open.\n * - Each lesson row shows a type icon (video / reading, named for screen\n * readers), its title, its duration as «m:ss» / «h:mm:ss», and — on a\n * free-preview lesson — «Vista previa gratuita» as a call to action:\n * a link (`getPreviewHref` / `previewHrefTemplate`), a button (`onPreview`),\n * or a plain label when the SITE wires neither.\n * - A module without lessons is a plain row (title only), never a button.\n *\n * It renders only what the API serves: published lessons, no video, playback\n * or attachment data. The preview itself is fetched by the page it links to\n * (`getLessonPreview` in `@reblu/site-next/academy-server`).\n *\n * Client Component: it holds the open/closed state. Copy and\n * `previewHrefTemplate` are serialisable, so a Server Component page can render\n * it directly; `getPreviewHref` / `onPreview` are functions, so they need a\n * Client Component parent.\n */\nexport interface CourseSyllabusLabels {\n heading: string\n moduleSingular: string\n modulePlural: string\n lessonSingular: string\n lessonPlural: string\n /** Call to action (or label) on a lesson with `isFreePreview`. */\n freePreview: string\n /** Hours unit in module/course totals («1 h 15 min»). */\n hours: string\n /** Minutes unit in module/course totals. */\n minutes: string\n expandAll: string\n collapseAll: string\n /** Screen-reader name of the video lesson icon. */\n videoLesson: string\n /** Screen-reader name of the text (reading) lesson icon. */\n textLesson: string\n}\n\nexport const DEFAULT_COURSE_SYLLABUS_LABELS: CourseSyllabusLabels = {\n heading: 'Programa',\n moduleSingular: 'módulo',\n modulePlural: 'módulos',\n lessonSingular: 'lección',\n lessonPlural: 'lecciones',\n freePreview: 'Vista previa gratuita',\n hours: 'h',\n minutes: 'min',\n expandAll: 'Expandir todo',\n collapseAll: 'Contraer todo',\n videoLesson: 'Vídeo',\n textLesson: 'Lectura',\n}\n\n/** A module as served. `lessons` is optional so an older payload still renders. */\nexport type CourseSyllabusModule = Pick<AcademyModulePublic, 'id' | 'title'> & {\n description?: string | null\n lessons?: readonly AcademyLessonPublic[]\n}\n\nexport interface CourseSyllabusProps {\n /** `AcademyCourseDetail.modules`, in the order the API returns them. */\n modules: readonly CourseSyllabusModule[]\n labels?: Partial<CourseSyllabusLabels>\n /** Level of the heading. Default `2`. */\n headingLevel?: HeadingLevel\n /** Replaces the heading's default typography (see the className rule in the README). */\n headingClassName?: string\n className?: string\n /**\n * Link target of a free-preview lesson, e.g. `/cursos/${slug}/preview/${l.slug}`.\n * Called only for lessons with `isFreePreview`. Return `undefined` to keep the label.\n */\n getPreviewHref?: (lesson: AcademyLessonPublic) => string | undefined\n /**\n * Serialisable alternative to `getPreviewHref` for Server Component pages:\n * `{lessonSlug}` and `{lessonId}` are replaced (URL-encoded). Ignored when\n * `getPreviewHref` is given.\n */\n previewHrefTemplate?: string\n /** Renders the call to action as a `<button>` (e.g. to open a modal). Used when no href resolves. */\n onPreview?: (lesson: AcademyLessonPublic) => void\n}\n\nconst SEPARATOR = ' · '\n\nfunction count(n: number, singular: string, plural: string): string {\n return `${n} ${n === 1 ? singular : plural}`\n}\n\n/** Module / course total: «15 min», «1 h», «1 h 15 min». */\nfunction formatTotal(seconds: number, labels: CourseSyllabusLabels): string {\n const totalMinutes = Math.max(1, Math.round(seconds / 60))\n const hours = Math.floor(totalMinutes / 60)\n const minutes = totalMinutes % 60\n if (hours === 0) return `${minutes} ${labels.minutes}`\n return minutes === 0\n ? `${hours} ${labels.hours}`\n : `${hours} ${labels.hours} ${minutes} ${labels.minutes}`\n}\n\nfunction splitSeconds(seconds: number): [number, number, number] {\n const whole = Math.round(seconds)\n return [Math.floor(whole / 3600), Math.floor((whole % 3600) / 60), whole % 60]\n}\n\n/** Lesson length: «m:ss», or «h:mm:ss» from one hour. */\nfunction formatClock(seconds: number): string {\n const [h, m, s] = splitSeconds(seconds)\n const ss = String(s).padStart(2, '0')\n return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`\n}\n\n/** ISO 8601 duration for `<time datetime>`: `PT10M0S`, `PT1H15M0S`. */\nfunction isoDuration(seconds: number): string {\n const [h, m, s] = splitSeconds(seconds)\n return `PT${h > 0 ? `${h}H` : ''}${m}M${s}S`\n}\n\nfunction hasDuration(lesson: AcademyLessonPublic): lesson is AcademyLessonPublic & {\n durationSeconds: number\n} {\n return typeof lesson.durationSeconds === 'number' && lesson.durationSeconds > 0\n}\n\n/** Sum of the lessons' durations, or `null` unless every lesson has one. */\nfunction totalSeconds(lessons: readonly AcademyLessonPublic[]): number | null {\n if (lessons.length === 0) return null\n let total = 0\n for (const l of lessons) {\n if (!hasDuration(l)) return null\n total += l.durationSeconds\n }\n return total\n}\n\nfunction fillTemplate(template: string, lesson: AcademyLessonPublic): string {\n return template\n .replaceAll('{lessonSlug}', encodeURIComponent(lesson.slug))\n .replaceAll('{lessonId}', encodeURIComponent(lesson.id))\n}\n\nconst ICON_PROPS = {\n 'aria-hidden': true,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n} as const\n\nfunction LessonIcon({ type }: { type: 'video' | 'text' }) {\n return (\n <svg\n {...ICON_PROPS}\n data-slot='course-syllabus-lesson-icon'\n data-type={type}\n className='size-4 shrink-0 text-muted-foreground'\n >\n {type === 'video' ? (\n <>\n <circle cx='12' cy='12' r='9' />\n <path d='m10 8.5 5 3.5-5 3.5z' />\n </>\n ) : (\n <>\n <path d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z' />\n <path d='M14 3v5h5M9 13h6M9 17h4' />\n </>\n )}\n </svg>\n )\n}\n\nfunction PlayIcon() {\n return (\n <svg {...ICON_PROPS} className='size-3.5 shrink-0'>\n <path d='m7 4 13 8-13 8z' fill='currentColor' />\n </svg>\n )\n}\n\nconst PREVIEW_CTA =\n 'inline-flex items-center gap-1.5 rounded-full border border-primary/40 bg-primary/10 px-3 py-1 text-xs font-semibold text-primary transition-colors hover:bg-primary hover:text-primary-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none'\n\nexport function CourseSyllabus({\n modules,\n labels: labelOverrides,\n headingLevel = 2,\n headingClassName,\n className,\n getPreviewHref,\n previewHrefTemplate,\n onPreview,\n}: CourseSyllabusProps) {\n const labels = { ...DEFAULT_COURSE_SYLLABUS_LABELS, ...labelOverrides }\n const baseId = useId()\n const [open, setOpen] = useState<ReadonlySet<string>>(() => new Set())\n\n if (modules.length === 0) return null\n\n const expandable = modules.filter((m) => (m.lessons?.length ?? 0) > 0).map((m) => m.id)\n const allLessons = modules.flatMap((m) => m.lessons ?? [])\n const courseSeconds = totalSeconds(allLessons)\n const summary = [count(modules.length, labels.moduleSingular, labels.modulePlural)]\n if (allLessons.length > 0)\n summary.push(count(allLessons.length, labels.lessonSingular, labels.lessonPlural))\n if (courseSeconds !== null) summary.push(formatTotal(courseSeconds, labels))\n\n const allOpen = expandable.length > 0 && expandable.every((id) => open.has(id))\n const headingId = `${baseId}-heading`\n const Tag = `h${headingLevel}` as const\n\n function toggle(id: string) {\n setOpen((prev) => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n function previewHref(lesson: AcademyLessonPublic): string | undefined {\n if (getPreviewHref) return getPreviewHref(lesson)\n return previewHrefTemplate ? fillTemplate(previewHrefTemplate, lesson) : undefined\n }\n\n function renderPreview(lesson: AcademyLessonPublic) {\n const name = `${labels.freePreview}: ${lesson.title}`\n const href = previewHref(lesson)\n if (href) {\n return (\n <a href={href} aria-label={name} data-slot='course-syllabus-preview' className={PREVIEW_CTA}>\n <PlayIcon />\n {labels.freePreview}\n </a>\n )\n }\n if (onPreview) {\n return (\n <button\n type='button'\n aria-label={name}\n data-slot='course-syllabus-preview'\n onClick={() => onPreview(lesson)}\n className={cn(PREVIEW_CTA, 'cursor-pointer')}\n >\n <PlayIcon />\n {labels.freePreview}\n </button>\n )\n }\n return (\n <span\n data-slot='course-syllabus-preview'\n className='rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-muted-foreground'\n >\n {labels.freePreview}\n </span>\n )\n }\n\n return (\n <section\n data-slot='course-syllabus'\n aria-labelledby={headingId}\n className={cn('space-y-4', className)}\n >\n <div\n data-slot='course-syllabus-header'\n className='flex flex-wrap items-end justify-between gap-x-4 gap-y-2'\n >\n <div className='space-y-1'>\n <Tag\n id={headingId}\n className={slotClassName('', 'text-xl font-semibold', headingClassName)}\n >\n {labels.heading}\n </Tag>\n <p data-slot='course-syllabus-summary' className='text-sm text-muted-foreground'>\n {summary.join(SEPARATOR)}\n </p>\n </div>\n {expandable.length > 0 && (\n <button\n type='button'\n data-slot='course-syllabus-toggle-all'\n onClick={() => setOpen(allOpen ? new Set() : new Set(expandable))}\n className='cursor-pointer rounded-md px-2 py-1 text-sm font-semibold text-primary underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'\n >\n {allOpen ? labels.collapseAll : labels.expandAll}\n </button>\n )}\n </div>\n\n <ol className='list-none divide-y divide-border overflow-hidden rounded-xl border border-border bg-card p-0'>\n {modules.map((module, index) => {\n const lessons = module.lessons ?? []\n const number = (\n <span\n aria-hidden='true'\n className='flex size-8 shrink-0 items-center justify-center rounded-full bg-primary/10 text-sm font-semibold text-primary tabular-nums'\n >\n {index + 1}\n </span>\n )\n\n if (lessons.length === 0) {\n return (\n <li\n key={module.id}\n data-slot='course-syllabus-module'\n className='flex items-center gap-4 px-4 py-4 sm:px-5'\n >\n {number}\n <span className='font-semibold leading-snug'>{module.title}</span>\n </li>\n )\n }\n\n const isOpen = open.has(module.id)\n const panelId = `${baseId}-module-${index}`\n const seconds = totalSeconds(lessons)\n const meta = [count(lessons.length, labels.lessonSingular, labels.lessonPlural)]\n if (seconds !== null) meta.push(formatTotal(seconds, labels))\n\n return (\n <li key={module.id} data-slot='course-syllabus-module'>\n <button\n type='button'\n aria-expanded={isOpen}\n aria-controls={panelId}\n onClick={() => toggle(module.id)}\n className={cn(\n 'flex w-full cursor-pointer items-center gap-4 px-4 py-4 text-left transition-colors hover:bg-muted/60 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none sm:px-5',\n isOpen && 'bg-muted/40',\n )}\n >\n {number}\n <span className='flex min-w-0 flex-1 flex-col gap-0.5'>\n <span className='font-semibold leading-snug'>{module.title}</span>\n <span className='text-sm text-muted-foreground'>{meta.join(SEPARATOR)}</span>\n </span>\n <svg\n {...ICON_PROPS}\n data-slot='course-syllabus-chevron'\n className={cn(\n 'size-5 shrink-0 text-muted-foreground transition-transform duration-200 motion-reduce:transition-none',\n isOpen && 'rotate-180',\n )}\n >\n <path d='m6 9 6 6 6-6' />\n </svg>\n </button>\n <ol\n id={panelId}\n hidden={!isOpen}\n data-slot='course-syllabus-lessons'\n className='list-none border-t border-border p-0 py-1'\n >\n {lessons.map((lesson) => {\n const type = lesson.hasVideo ? 'video' : 'text'\n return (\n <li\n key={lesson.id}\n data-slot='course-syllabus-lesson'\n className='flex flex-wrap items-center gap-x-3 gap-y-1.5 px-4 py-2.5 text-sm sm:pl-[4.25rem] sm:pr-5'\n >\n <LessonIcon type={type} />\n <span className='sr-only'>\n {type === 'video' ? labels.videoLesson : labels.textLesson}\n </span>\n <span\n data-slot='course-syllabus-lesson-title'\n className='min-w-0 flex-1 leading-snug'\n >\n {lesson.title}\n </span>\n {lesson.isFreePreview && renderPreview(lesson)}\n {hasDuration(lesson) && (\n <time\n data-slot='course-syllabus-lesson-duration'\n dateTime={isoDuration(lesson.durationSeconds)}\n className='ml-auto shrink-0 text-muted-foreground tabular-nums'\n >\n {formatClock(lesson.durationSeconds)}\n </time>\n )}\n </li>\n )\n })}\n </ol>\n </li>\n )\n })}\n </ol>\n </section>\n )\n}\n","import { cn } from '@reblu/site-ui/utils'\n\n/**\n * `text-*` utilities tailwind-merge classifies correctly (alignment, wrapping,\n * overflow). Every other `text-*` may be a SITE theme token it misreads.\n */\nconst MERGE_SAFE_TEXT = /(?:^|:)!?text-(?:left|center|right|justify|start|end|wrap|nowrap|balance|pretty|ellipsis|clip)$/\nconst TEXT_UTILITY = /(?:^|:)!?-?text-/\n\nfunction bypassesMerge(token: string): boolean {\n return TEXT_UTILITY.test(token) && !MERGE_SAFE_TEXT.test(token)\n}\n\n/**\n * Classes for a drop-in element a consumer can style (REB-997).\n *\n * tailwind-merge only knows Tailwind's default scale: a SITE's custom font-size\n * token such as `text-h2` is read as a text COLOUR and silently dropped against\n * a real colour (`twMerge('text-h2 text-ink')` → `text-ink`). So:\n *\n * - no consumer class (`undefined` or blank) → `cn(structural, defaults)`,\n * exactly what the component rendered before;\n * - with a consumer class → the stylistic `defaults` are NOT applied (the\n * consumer owns the look of that slot); `structural` — the classes the\n * element needs to work — is kept and merged with the consumer's layout\n * classes, while the consumer's `text-*` classes skip tailwind-merge and are\n * appended verbatim, so custom tokens reach the DOM intact.\n */\nexport function slotClassName(structural: string, defaults: string, consumer: string | undefined): string {\n if (consumer === undefined || consumer.trim() === '') return cn(structural, defaults)\n const tokens = consumer.split(/\\s+/).filter(Boolean)\n const verbatim = tokens.filter(bypassesMerge)\n const merged = cn(structural, tokens.filter((token) => !bypassesMerge(token)))\n return [merged, ...verbatim].filter(Boolean).join(' ')\n}\n","import { useEffect, useId, useRef, useState, type FormEvent, type ReactNode } from 'react'\nimport { cn } from '@reblu/site-ui/utils'\nimport {\n DATA_REQUEST_TYPES,\n type DataRequestInput,\n type DataRequestSubmission,\n type DataRequestType,\n} from '@reblu/site-contracts'\n\n/**\n * `DataRequestForm` — drop-in public GDPR rights form (REB-980) over\n * `POST /api/site/data-requests` (REB-77). The visitor picks the right they\n * exercise, identifies themselves, accepts the processing notice and submits;\n * the request lands in the tenant's nexus queue with source `PUBLIC_FORM` and\n * its 72h clock running, and the form shows the response deadline.\n *\n * Stateful (client-only — ships from `./client`) but transport-agnostic:\n * - `getCaptchaToken` mints the reCAPTCHA v3 token. base-api rejects a\n * request without one, so the form refuses to submit when it resolves\n * `undefined`. Pair it with `useDeferredRecaptchaToken` from\n * `@reblu/site-next/recaptcha-provider`.\n * - `submit` sends the body. By default it POSTs JSON to the SITE's own\n * Route Handler (`endpoint`, default `/api/data-requests`) and unwraps the\n * `{ data }` envelope — the API key never reaches the browser.\n *\n * Errors never show server text: a rejection is mapped by HTTP status to the\n * fixed (overridable) copy in `labels`, so nothing upstream — validation\n * details, provider state — reaches the visitor.\n */\nexport type DataRequestSubmitResult =\n | { ok: true; data: DataRequestSubmission }\n /** `status` is the HTTP status, or `0` when the request never got an answer. */\n | { ok: false; status: number }\n\n/**\n * Every GDPR right, in base-api's order. Lives in `@reblu/site-contracts`\n * since REB-1005 (server code needs it too); re-exported here so existing\n * `@reblu/site-blocks/client` imports keep working.\n */\nexport { DATA_REQUEST_TYPES }\n\n/** The server's field limits (base-api `POST /api/site/data-requests`). */\nexport const DATA_REQUEST_FIELD_LIMITS = Object.freeze({ name: 200, email: 320, details: 5000 })\n\nexport interface DataRequestFormLabels {\n requestTypeLabel: string\n requestTypePlaceholder: string\n requestTypes: Record<DataRequestType, string>\n nameLabel: string\n emailLabel: string\n detailsLabel: string\n detailsPlaceholder: string\n consent: ReactNode\n submit: string\n submitting: string\n requestTypeRequired: string\n nameRequired: string\n emailInvalid: string\n consentRequired: string\n /** Shown on success; receives the formatted 72h deadline. */\n success: (deadline: string) => ReactNode\n captchaError: string\n validationError: string\n rateLimitError: string\n genericError: string\n}\n\nexport const DEFAULT_DATA_REQUEST_FORM_LABELS: DataRequestFormLabels = {\n requestTypeLabel: 'Derecho que quieres ejercer',\n requestTypePlaceholder: 'Selecciona una opción',\n requestTypes: {\n ACCESS: 'Acceso',\n RECTIFICATION: 'Rectificación',\n ERASURE: 'Supresión',\n OBJECTION: 'Oposición',\n PORTABILITY: 'Portabilidad',\n RESTRICTION: 'Limitación del tratamiento',\n AUTOMATED_DECISION: 'No ser objeto de decisiones automatizadas',\n INFORMATION: 'Información',\n },\n nameLabel: 'Nombre y apellidos',\n emailLabel: 'Email',\n detailsLabel: 'Mensaje (opcional)',\n detailsPlaceholder: 'Describe tu solicitud',\n consent: 'Acepto que mis datos se traten para gestionar esta solicitud.',\n submit: 'Enviar solicitud',\n submitting: 'Enviando…',\n requestTypeRequired: 'Elige el derecho que quieres ejercer.',\n nameRequired: 'Indica tu nombre.',\n emailInvalid: 'Introduce un email válido.',\n consentRequired: 'Debes aceptar el tratamiento de tus datos para gestionar la solicitud.',\n success: (deadline) => (\n <>\n Hemos recibido tu solicitud. Te responderemos como máximo el {deadline}.\n </>\n ),\n captchaError:\n 'No hemos podido verificar que no eres un robot. Recarga la página e inténtalo de nuevo.',\n validationError: 'Revisa los datos del formulario e inténtalo de nuevo.',\n rateLimitError: 'Has enviado demasiadas solicitudes. Espera unos minutos e inténtalo de nuevo.',\n genericError: 'No hemos podido enviar tu solicitud. Inténtalo de nuevo más tarde.',\n}\n\nexport interface DataRequestFormProps {\n /** Mints the reCAPTCHA v3 token; `undefined` blocks the submission. */\n getCaptchaToken: () => Promise<string | undefined>\n /** Sends the body. Defaults to a JSON POST to `endpoint`. */\n submit?: (input: DataRequestInput) => Promise<DataRequestSubmitResult>\n /** The SITE's Route Handler used by the default `submit`. @default '/api/data-requests' */\n endpoint?: string\n /** First interaction with the form — wire it to the deferred reCAPTCHA `arm`. */\n onInteract?: () => void\n /** Called with the acknowledgement after a successful submission. */\n onSubmitted?: (ack: DataRequestSubmission) => void\n /** Rights offered, in order. @default DATA_REQUEST_TYPES */\n requestTypes?: readonly DataRequestType[]\n labels?: Partial<DataRequestFormLabels>\n /** Locale for the deadline. @default 'es-ES' */\n locale?: string\n className?: string\n}\n\ntype FieldName = 'requestType' | 'name' | 'email' | 'consent'\ntype FieldErrors = Partial<Record<FieldName, string>>\n\n/** Form order — focus goes to the first invalid field in this order. */\nconst FIELD_ORDER: readonly FieldName[] = ['requestType', 'name', 'email', 'consent']\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nasync function postToRoute(endpoint: string, input: DataRequestInput): Promise<DataRequestSubmitResult> {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) return { ok: false, status: res.status }\n const json = (await res.json()) as { data?: DataRequestSubmission }\n return json.data ? { ok: true, data: json.data } : { ok: false, status: res.status }\n } catch {\n return { ok: false, status: 0 }\n }\n}\n\nfunction rejectionMessage(status: number, t: DataRequestFormLabels): string {\n if (status === 400) return t.validationError\n if (status === 422) return t.captchaError\n if (status === 429) return t.rateLimitError\n return t.genericError\n}\n\nconst FIELD_CLASS =\n 'w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20'\n\nexport function DataRequestForm({\n getCaptchaToken,\n submit,\n endpoint = '/api/data-requests',\n onInteract,\n onSubmitted,\n requestTypes = DATA_REQUEST_TYPES,\n labels,\n locale = 'es-ES',\n className,\n}: DataRequestFormProps) {\n const t: DataRequestFormLabels = {\n ...DEFAULT_DATA_REQUEST_FORM_LABELS,\n ...labels,\n requestTypes: { ...DEFAULT_DATA_REQUEST_FORM_LABELS.requestTypes, ...labels?.requestTypes },\n }\n const id = useId()\n const [requestType, setRequestType] = useState<DataRequestType | ''>('')\n const [name, setName] = useState('')\n const [email, setEmail] = useState('')\n const [details, setDetails] = useState('')\n const [consent, setConsent] = useState(false)\n const [errors, setErrors] = useState<FieldErrors>({})\n const [formError, setFormError] = useState<string | null>(null)\n const [submitting, setSubmitting] = useState(false)\n const [ack, setAck] = useState<DataRequestSubmission | null>(null)\n const statusRef = useRef<HTMLDivElement>(null)\n\n // REB-1003: the success message replaces the form, so focus would otherwise\n // fall back to <body> — move it to the status the screen reader announces.\n useEffect(() => {\n if (ack) statusRef.current?.focus()\n }, [ack])\n\n async function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault()\n if (submitting) return\n setFormError(null)\n\n const next: FieldErrors = {}\n if (!requestType) next.requestType = t.requestTypeRequired\n if (!name.trim()) next.name = t.nameRequired\n if (!EMAIL_PATTERN.test(email.trim())) next.email = t.emailInvalid\n if (!consent) next.consent = t.consentRequired\n setErrors(next)\n const firstInvalid = FIELD_ORDER.find((field) => next[field])\n if (firstInvalid) {\n // REB-1003: take the visitor to the first field to fix.\n document.getElementById(`${id}-${firstInvalid === 'requestType' ? 'type' : firstInvalid}`)?.focus()\n return\n }\n if (!requestType) return\n\n setSubmitting(true)\n try {\n const captcha = await getCaptchaToken().catch(() => undefined)\n if (!captcha) {\n setFormError(t.captchaError)\n return\n }\n const input: DataRequestInput = {\n requestType,\n name: name.trim(),\n email: email.trim(),\n ...(details.trim() ? { details: details.trim() } : {}),\n captcha,\n }\n const result = await (submit ?? ((body) => postToRoute(endpoint, body)))(input).catch(\n (): DataRequestSubmitResult => ({ ok: false, status: 0 }),\n )\n if (!result.ok) {\n setFormError(rejectionMessage(result.status, t))\n return\n }\n setAck(result.data)\n onSubmitted?.(result.data)\n } finally {\n setSubmitting(false)\n }\n }\n\n if (ack) {\n const deadline = new Intl.DateTimeFormat(locale, {\n dateStyle: 'long',\n timeStyle: 'short',\n }).format(new Date(ack.slaDueAt))\n return (\n <div\n ref={statusRef}\n tabIndex={-1}\n role='status'\n data-slot='data-request-form-success'\n className={cn('rounded-lg border border-border p-6 text-sm', className)}>\n {t.success(deadline)}\n </div>\n )\n }\n\n const errorId = (field: keyof FieldErrors) => (errors[field] ? `${id}-${field}-error` : undefined)\n const fieldError = (field: keyof FieldErrors) =>\n errors[field] ? (\n <p id={`${id}-${field}-error`} className='text-xs text-destructive'>\n {errors[field]}\n </p>\n ) : null\n\n return (\n <form\n noValidate\n onSubmit={handleSubmit}\n onFocusCapture={onInteract}\n onPointerDown={onInteract}\n data-slot='data-request-form'\n className={cn('space-y-4', className)}\n >\n <div className='space-y-1'>\n <label htmlFor={`${id}-type`} className='text-sm font-medium'>\n {t.requestTypeLabel}\n </label>\n <select\n id={`${id}-type`}\n value={requestType}\n onChange={(e) => setRequestType(e.target.value as DataRequestType | '')}\n aria-invalid={errors.requestType ? true : undefined}\n aria-describedby={errorId('requestType')}\n className={cn(FIELD_CLASS, errors.requestType ? 'border-destructive' : 'border-border')}\n >\n <option value=''>{t.requestTypePlaceholder}</option>\n {requestTypes.map((type) => (\n <option key={type} value={type}>\n {t.requestTypes[type]}\n </option>\n ))}\n </select>\n {fieldError('requestType')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-name`} className='text-sm font-medium'>\n {t.nameLabel}\n </label>\n <input\n id={`${id}-name`}\n type='text'\n autoComplete='name'\n maxLength={DATA_REQUEST_FIELD_LIMITS.name}\n value={name}\n onChange={(e) => setName(e.target.value)}\n aria-invalid={errors.name ? true : undefined}\n aria-describedby={errorId('name')}\n className={cn(FIELD_CLASS, errors.name ? 'border-destructive' : 'border-border')}\n />\n {fieldError('name')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-email`} className='text-sm font-medium'>\n {t.emailLabel}\n </label>\n <input\n id={`${id}-email`}\n type='email'\n autoComplete='email'\n maxLength={DATA_REQUEST_FIELD_LIMITS.email}\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n aria-invalid={errors.email ? true : undefined}\n aria-describedby={errorId('email')}\n className={cn(FIELD_CLASS, errors.email ? 'border-destructive' : 'border-border')}\n />\n {fieldError('email')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-details`} className='text-sm font-medium'>\n {t.detailsLabel}\n </label>\n <textarea\n id={`${id}-details`}\n rows={4}\n maxLength={DATA_REQUEST_FIELD_LIMITS.details}\n value={details}\n onChange={(e) => setDetails(e.target.value)}\n placeholder={t.detailsPlaceholder}\n className={cn(FIELD_CLASS, 'border-border')}\n />\n </div>\n\n <div className='space-y-1'>\n <div className='flex items-start gap-2'>\n <input\n id={`${id}-consent`}\n type='checkbox'\n checked={consent}\n onChange={(e) => setConsent(e.target.checked)}\n aria-invalid={errors.consent ? true : undefined}\n aria-describedby={errorId('consent')}\n className='mt-1'\n />\n <label htmlFor={`${id}-consent`} className='text-sm text-foreground/80'>\n {t.consent}\n </label>\n </div>\n {fieldError('consent')}\n </div>\n\n {formError && (\n <p role='alert' className='text-sm text-destructive'>\n {formError}\n </p>\n )}\n\n <button\n type='submit'\n disabled={submitting}\n className='cursor-pointer rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:cursor-not-allowed disabled:opacity-60'\n >\n {submitting ? t.submitting : t.submit}\n </button>\n </form>\n )\n}\n","import { useEffect, useId, useRef, useState, type FormEvent, type ReactNode } from 'react'\nimport { cn } from '@reblu/site-ui/utils'\nimport type { WithdrawalRequestInput, WithdrawalRequestSubmission } from '@reblu/site-contracts'\n\n/**\n * `WithdrawalRequestForm` — drop-in withdrawal form (REB-1201) over\n * `POST /api/site/withdrawal-requests`. The buyer gives the order reference,\n * their email, what they withdraw from and, optionally, a reason; base-api\n * stores it, emails the tenant team and sends the buyer an acknowledgement.\n *\n * It decides nothing: it does not check the order, nor exclude any product\n * or service. Whether the right applies is the tenant's decision, explained on\n * its own withdrawal page (`LegalDocument` over `legal.withdrawalPolicy`).\n *\n * Stateful (client-only — ships from `./client`) but transport-agnostic, like\n * `DataRequestForm`:\n * - `getCaptchaToken` mints the reCAPTCHA v3 token; `undefined` blocks the\n * submission (base-api rejects a request without one).\n * - `submit` sends the body. By default it POSTs JSON to the SITE's own Route\n * Handler (`endpoint`, default `/api/withdrawal-requests`) and unwraps the\n * `{ data }` envelope — the API key never reaches the browser.\n *\n * On success the form is replaced by a receipt. When the acknowledgement email\n * could not be sent, the receipt repeats what was communicated so the buyer\n * can keep it. Errors never show server text: they map by HTTP status to the\n * fixed (overridable) copy in `labels`.\n */\nexport type WithdrawalRequestSubmitResult =\n | { ok: true; data: WithdrawalRequestSubmission }\n /** `status` is the HTTP status, or `0` when the request never got an answer. */\n | { ok: false; status: number }\n\n/** The server's field limits (base-api `POST /api/site/withdrawal-requests`). */\nexport const WITHDRAWAL_REQUEST_FIELD_LIMITS = Object.freeze({\n orderReference: 100,\n email: 320,\n name: 200,\n items: 2000,\n reason: 2000,\n})\n\nexport interface WithdrawalRequestFormLabels {\n orderReferenceLabel: string\n nameLabel: string\n emailLabel: string\n itemsLabel: string\n itemsPlaceholder: string\n reasonLabel: string\n /** Small print under the fields, e.g. how the data is used. `null` hides it. */\n notice: ReactNode\n submit: string\n submitting: string\n orderReferenceRequired: string\n emailInvalid: string\n itemsRequired: string\n /** Receipt when the buyer was emailed a copy; receives the formatted received-at date. */\n success: (receivedAt: string) => ReactNode\n /** Receipt when no copy could be emailed; the communication is listed below it. */\n successWithoutEmail: (receivedAt: string) => ReactNode\n receiptOrderReference: string\n receiptItems: string\n captchaError: string\n validationError: string\n rateLimitError: string\n genericError: string\n}\n\nexport const DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS: WithdrawalRequestFormLabels = {\n orderReferenceLabel: 'Número de pedido',\n nameLabel: 'Nombre y apellidos (opcional)',\n emailLabel: 'Email',\n itemsLabel: 'Qué productos o servicios quieres devolver o cancelar',\n itemsPlaceholder: 'Indica los productos, bonos o cursos y las unidades',\n reasonLabel: 'Motivo (opcional)',\n notice: 'Usaremos estos datos solo para gestionar tu desistimiento.',\n submit: 'Enviar desistimiento',\n submitting: 'Enviando…',\n orderReferenceRequired: 'Indica el número de pedido.',\n emailInvalid: 'Introduce un email válido.',\n itemsRequired: 'Indica de qué productos o servicios desistes.',\n success: (receivedAt) => (\n <>Hemos recibido tu desistimiento el {receivedAt}. Te hemos enviado una copia por email.</>\n ),\n successWithoutEmail: (receivedAt) => (\n <>\n Hemos recibido tu desistimiento el {receivedAt}. Guarda esta pantalla como justificante de lo\n que nos has comunicado:\n </>\n ),\n receiptOrderReference: 'Pedido',\n receiptItems: 'Productos o servicios',\n captchaError:\n 'No hemos podido verificar que no eres un robot. Recarga la página e inténtalo de nuevo.',\n validationError: 'Revisa los datos del formulario e inténtalo de nuevo.',\n rateLimitError: 'Has enviado demasiadas solicitudes. Espera unos minutos e inténtalo de nuevo.',\n genericError: 'No hemos podido enviar tu desistimiento. Inténtalo de nuevo más tarde.',\n}\n\nexport interface WithdrawalRequestFormProps {\n /** Mints the reCAPTCHA v3 token; `undefined` blocks the submission. */\n getCaptchaToken: () => Promise<string | undefined>\n /** Sends the body. Defaults to a JSON POST to `endpoint`. */\n submit?: (input: WithdrawalRequestInput) => Promise<WithdrawalRequestSubmitResult>\n /** The SITE's Route Handler used by the default `submit`. @default '/api/withdrawal-requests' */\n endpoint?: string\n /** First interaction with the form — wire it to the deferred reCAPTCHA `arm`. */\n onInteract?: () => void\n /** Called with the acknowledgement after a successful submission. */\n onSubmitted?: (ack: WithdrawalRequestSubmission) => void\n labels?: Partial<WithdrawalRequestFormLabels>\n /** Locale for the received-at date. @default 'es-ES' */\n locale?: string\n className?: string\n}\n\ntype FieldName = 'orderReference' | 'email' | 'items'\ntype FieldErrors = Partial<Record<FieldName, string>>\n\n/** Form order — focus goes to the first invalid field in this order. */\nconst FIELD_ORDER: readonly FieldName[] = ['orderReference', 'email', 'items']\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nasync function postToRoute(\n endpoint: string,\n input: WithdrawalRequestInput,\n): Promise<WithdrawalRequestSubmitResult> {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) return { ok: false, status: res.status }\n const json = (await res.json()) as { data?: WithdrawalRequestSubmission }\n return json.data ? { ok: true, data: json.data } : { ok: false, status: res.status }\n } catch {\n return { ok: false, status: 0 }\n }\n}\n\nfunction rejectionMessage(status: number, t: WithdrawalRequestFormLabels): string {\n if (status === 400) return t.validationError\n if (status === 422) return t.captchaError\n if (status === 429) return t.rateLimitError\n return t.genericError\n}\n\nconst FIELD_CLASS =\n 'w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20'\n\ninterface Receipt {\n ack: WithdrawalRequestSubmission\n orderReference: string\n items: string\n}\n\nexport function WithdrawalRequestForm({\n getCaptchaToken,\n submit,\n endpoint = '/api/withdrawal-requests',\n onInteract,\n onSubmitted,\n labels,\n locale = 'es-ES',\n className,\n}: WithdrawalRequestFormProps) {\n const t: WithdrawalRequestFormLabels = { ...DEFAULT_WITHDRAWAL_REQUEST_FORM_LABELS, ...labels }\n const id = useId()\n const [orderReference, setOrderReference] = useState('')\n const [name, setName] = useState('')\n const [email, setEmail] = useState('')\n const [items, setItems] = useState('')\n const [reason, setReason] = useState('')\n const [errors, setErrors] = useState<FieldErrors>({})\n const [formError, setFormError] = useState<string | null>(null)\n const [submitting, setSubmitting] = useState(false)\n const [receipt, setReceipt] = useState<Receipt | null>(null)\n const statusRef = useRef<HTMLDivElement>(null)\n\n // The receipt replaces the form: move focus to the status it announces.\n useEffect(() => {\n if (receipt) statusRef.current?.focus()\n }, [receipt])\n\n async function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault()\n if (submitting) return\n setFormError(null)\n\n const next: FieldErrors = {}\n if (!orderReference.trim()) next.orderReference = t.orderReferenceRequired\n if (!EMAIL_PATTERN.test(email.trim())) next.email = t.emailInvalid\n if (!items.trim()) next.items = t.itemsRequired\n setErrors(next)\n const firstInvalid = FIELD_ORDER.find((field) => next[field])\n if (firstInvalid) {\n document.getElementById(`${id}-${firstInvalid}`)?.focus()\n return\n }\n\n setSubmitting(true)\n try {\n const captcha = await getCaptchaToken().catch(() => undefined)\n if (!captcha) {\n setFormError(t.captchaError)\n return\n }\n const input: WithdrawalRequestInput = {\n orderReference: orderReference.trim(),\n email: email.trim(),\n ...(name.trim() ? { name: name.trim() } : {}),\n items: items.trim(),\n ...(reason.trim() ? { reason: reason.trim() } : {}),\n captcha,\n }\n const result = await (submit ?? ((body) => postToRoute(endpoint, body)))(input).catch(\n (): WithdrawalRequestSubmitResult => ({ ok: false, status: 0 }),\n )\n if (!result.ok) {\n setFormError(rejectionMessage(result.status, t))\n return\n }\n setReceipt({ ack: result.data, orderReference: input.orderReference, items: input.items })\n onSubmitted?.(result.data)\n } finally {\n setSubmitting(false)\n }\n }\n\n if (receipt) {\n const receivedAt = new Intl.DateTimeFormat(locale, {\n dateStyle: 'long',\n timeStyle: 'short',\n }).format(new Date(receipt.ack.receivedAt))\n return (\n <div\n ref={statusRef}\n tabIndex={-1}\n role='status'\n data-slot='withdrawal-request-form-success'\n className={cn('space-y-3 rounded-lg border border-border p-6 text-sm', className)}\n >\n {receipt.ack.acknowledgementSent ? (\n <p>{t.success(receivedAt)}</p>\n ) : (\n <>\n <p>{t.successWithoutEmail(receivedAt)}</p>\n <dl className='space-y-1'>\n <div>\n <dt className='font-medium'>{t.receiptOrderReference}</dt>\n <dd>{receipt.orderReference}</dd>\n </div>\n <div>\n <dt className='font-medium'>{t.receiptItems}</dt>\n <dd className='whitespace-pre-line'>{receipt.items}</dd>\n </div>\n </dl>\n </>\n )}\n </div>\n )\n }\n\n const errorId = (field: FieldName) => (errors[field] ? `${id}-${field}-error` : undefined)\n const fieldError = (field: FieldName) =>\n errors[field] ? (\n <p id={`${id}-${field}-error`} className='text-xs text-destructive'>\n {errors[field]}\n </p>\n ) : null\n const borderFor = (field: FieldName) => (errors[field] ? 'border-destructive' : 'border-border')\n\n return (\n <form\n noValidate\n onSubmit={handleSubmit}\n onFocusCapture={onInteract}\n onPointerDown={onInteract}\n data-slot='withdrawal-request-form'\n className={cn('space-y-4', className)}\n >\n <div className='space-y-1'>\n <label htmlFor={`${id}-orderReference`} className='text-sm font-medium'>\n {t.orderReferenceLabel}\n </label>\n <input\n id={`${id}-orderReference`}\n type='text'\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.orderReference}\n value={orderReference}\n onChange={(e) => setOrderReference(e.target.value)}\n aria-invalid={errors.orderReference ? true : undefined}\n aria-describedby={errorId('orderReference')}\n className={cn(FIELD_CLASS, borderFor('orderReference'))}\n />\n {fieldError('orderReference')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-name`} className='text-sm font-medium'>\n {t.nameLabel}\n </label>\n <input\n id={`${id}-name`}\n type='text'\n autoComplete='name'\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.name}\n value={name}\n onChange={(e) => setName(e.target.value)}\n className={cn(FIELD_CLASS, 'border-border')}\n />\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-email`} className='text-sm font-medium'>\n {t.emailLabel}\n </label>\n <input\n id={`${id}-email`}\n type='email'\n autoComplete='email'\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.email}\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n aria-invalid={errors.email ? true : undefined}\n aria-describedby={errorId('email')}\n className={cn(FIELD_CLASS, borderFor('email'))}\n />\n {fieldError('email')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-items`} className='text-sm font-medium'>\n {t.itemsLabel}\n </label>\n <textarea\n id={`${id}-items`}\n rows={3}\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.items}\n value={items}\n onChange={(e) => setItems(e.target.value)}\n placeholder={t.itemsPlaceholder}\n aria-invalid={errors.items ? true : undefined}\n aria-describedby={errorId('items')}\n className={cn(FIELD_CLASS, borderFor('items'))}\n />\n {fieldError('items')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor={`${id}-reason`} className='text-sm font-medium'>\n {t.reasonLabel}\n </label>\n <textarea\n id={`${id}-reason`}\n rows={3}\n maxLength={WITHDRAWAL_REQUEST_FIELD_LIMITS.reason}\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n className={cn(FIELD_CLASS, 'border-border')}\n />\n </div>\n\n {t.notice ? <p className='text-xs text-muted-foreground'>{t.notice}</p> : null}\n\n {formError && (\n <p role='alert' className='text-sm text-destructive'>\n {formError}\n </p>\n )}\n\n <button\n type='submit'\n disabled={submitting}\n className='cursor-pointer rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:cursor-not-allowed disabled:opacity-60'\n >\n {submitting ? t.submitting : t.submit}\n </button>\n </form>\n )\n}\n","import { useId, useState, type FormEvent, type ReactNode } from 'react'\nimport { cn } from '@reblu/site-ui/utils'\nimport type {\n AcademyCheckoutErrorCode,\n AcademyCheckoutRequest,\n AcademyCheckoutSession,\n AcademyCourseDetail,\n ShopCheckoutCopy,\n} from '@reblu/site-contracts'\nimport { formatPrice } from '../lib/format-price'\nimport { toAcademyCheckoutInvoiceFields } from '../lib/course-guest-checkout'\nimport { CheckoutPayAction } from './checkout-pay-action'\nimport {\n CheckoutTermsAcceptance,\n toCheckoutTermsFields,\n type CheckoutTermsCopy,\n} from './checkout-terms-acceptance'\nimport {\n EMPTY_FULL_INVOICE_VALUE,\n FullInvoiceFields,\n isOverSimplifiedInvoiceCap,\n type FullInvoiceErrors,\n type FullInvoiceFieldsLabels,\n type FullInvoiceValue,\n} from './full-invoice-fields'\n\n/**\n * `CourseGuestCheckoutForm` (REB-1264) — drop-in guest checkout of a course\n * that needs no validation, over `POST /api/site/academy/checkout`: name,\n * surname, email, the tenant's terms box, the course's consent box\n * (`AcademyCourseDetail.withdrawalConsent`), the optional full invoice, the\n * total and the pay notice. On success the buyer goes to Stripe; the account\n * is created after payment.\n *\n * Stateful (client-only — ships from `./client`) but transport-agnostic, like\n * `WithdrawalRequestForm`: `submit` sends the body; by default it POSTs JSON\n * to the SITE's own Route Handler (`endpoint`, default `/api/academy/checkout`\n * — mount `createAcademyCheckoutRoute()` from `@reblu/site-next/route-handlers`\n * there), which adds the return URLs and the API key.\n *\n * When the server answers `ENROLLMENT_REQUEST_REQUIRED` (the course needs a\n * validation, or is free), the form gives way to `requestForm`, the SITE's\n * enrollment request form. Render `requestForm` straight away for a course\n * `courseSellsAsGuest` rejects.\n *\n * No enumeration: the form says the same for every email and never mentions\n * an account (REB-834). The copy is Spanish and neutral; replace any text\n * with `labels` / `errorMessages`.\n */\nexport type CourseGuestCheckoutInput = Omit<AcademyCheckoutRequest, 'successUrl' | 'cancelUrl'> &\n Partial<Pick<AcademyCheckoutRequest, 'successUrl' | 'cancelUrl'>>\n\nexport type CourseGuestCheckoutResult =\n | { ok: true; data: AcademyCheckoutSession }\n /** `status` is the HTTP status, or `0` when the request never got an answer. */\n | { ok: false; status: number; code?: string; details?: unknown }\n\n/** The tenant's checkout copy (`ShopConfig.checkout`): terms box, button, notice and invoice texts. */\nexport type CourseGuestCheckoutCopy = CheckoutTermsCopy &\n Partial<\n Pick<\n ShopCheckoutCopy,\n 'continueToPaymentLabel' | 'payNotice' | 'fullInvoiceCheckboxLabel' | 'fullInvoiceHelpText'\n >\n >\n\nexport interface CourseGuestCheckoutFormLabels {\n firstNameLabel: string\n lastNameLabel: string\n emailLabel: string\n /** The total line; receives the formatted price. */\n total: (formattedTotal: string) => ReactNode\n /** Button text when the copy carries none. */\n submit: string\n submitting: string\n firstNameRequired: string\n lastNameRequired: string\n emailInvalid: string\n termsRequired: string\n consentRequired: string\n /** Shown above `requestForm` after `ENROLLMENT_REQUEST_REQUIRED`. `null` hides it. */\n requestRequiredNotice: ReactNode\n genericError: string\n}\n\nexport const DEFAULT_COURSE_GUEST_CHECKOUT_FORM_LABELS: CourseGuestCheckoutFormLabels = {\n firstNameLabel: 'Nombre',\n lastNameLabel: 'Apellidos',\n emailLabel: 'Email',\n total: (total) => `Total (impuestos incluidos): ${total}`,\n submit: 'Continuar al pago',\n submitting: 'Procesando…',\n firstNameRequired: 'Indica tu nombre.',\n lastNameRequired: 'Indica tus apellidos.',\n emailInvalid: 'Introduce un email válido.',\n termsRequired: 'Para continuar, acepta las condiciones.',\n consentRequired: 'Para continuar, marca esta casilla.',\n requestRequiredNotice: 'Este curso se solicita antes de pagar. Completa la solicitud.',\n genericError: 'No hemos podido iniciar el pago. Inténtalo de nuevo.',\n}\n\n/** Default copy for every `AcademyCheckoutErrorCode`; override any subset with `errorMessages`. */\nexport const DEFAULT_COURSE_GUEST_CHECKOUT_ERROR_MESSAGES: Record<\n AcademyCheckoutErrorCode,\n string\n> = {\n INVALID_INPUT: 'Revisa los datos introducidos.',\n RATE_LIMITED: 'Demasiados intentos. Espera unos minutos e inténtalo de nuevo.',\n COURSE_NOT_FOUND: 'Este curso ya no está disponible.',\n SALES_CLOSED: 'Este curso no está a la venta en este momento.',\n ENROLLMENT_REQUEST_REQUIRED: 'Este curso se solicita antes de pagar.',\n INVOICING_NOT_READY: 'No se puede completar la compra en este momento. Inténtalo más tarde.',\n TERMS_NOT_ACCEPTED: 'Para continuar, acepta las condiciones.',\n TERMS_VERSION_CHANGED:\n 'Las condiciones se han actualizado. Recarga la página y vuelve a aceptarlas.',\n CONSENT_REQUIRED: 'Para continuar, marca la casilla del curso.',\n CONSENT_VERSION_CHANGED:\n 'El texto de la casilla ha cambiado. Recarga la página y vuelve a marcarla.',\n INVALID_BILLING: 'Revisa los datos de facturación.',\n}\n\nexport interface CourseGuestCheckoutFormProps {\n /**\n * The fields of `AcademyCourseDetail` the form reads. `withdrawalConsent` is\n * REQUIRED (pass `null` when the course has no box): omitting it for a\n * course that has one would send no version, and the server would refuse\n * every try with `CONSENT_VERSION_CHANGED`. The contract field is optional\n * (older APIs), so pass `course.withdrawalConsent ?? null`.\n */\n course: Pick<AcademyCourseDetail, 'slug' | 'priceCents'> & {\n withdrawalConsent: NonNullable<AcademyCourseDetail['withdrawalConsent']> | null\n }\n /** The tenant's checkout copy — pass `ShopConfig.checkout`. */\n copy: CourseGuestCheckoutCopy\n /** The SITE's terms page, used when the tenant set no `termsLinkUrl`. */\n termsHref: string\n /** The SITE's enrollment request form, shown on `ENROLLMENT_REQUEST_REQUIRED`. */\n requestForm: ReactNode\n /** `false` when the tenant issues no invoices: no full-invoice opt-in. @default true */\n issuesInvoices?: boolean\n /** The tenant's simplified-invoice cap in cents (`ShopConfig.simplifiedInvoiceCapCents`). */\n simplifiedInvoiceCapCents?: number | null\n /** Sends the body. Defaults to a JSON POST to `endpoint`. */\n submit?: (input: CourseGuestCheckoutInput) => Promise<CourseGuestCheckoutResult>\n /** The SITE's Route Handler used by the default `submit`. @default '/api/academy/checkout' */\n endpoint?: string\n /** Called with the Stripe URL. @default `window.location.assign(url)` */\n onRedirect?: (url: string) => void\n /** Called with every refusal, e.g. to reload the page on a `*_VERSION_CHANGED`. */\n onError?: (result: Extract<CourseGuestCheckoutResult, { ok: false }>) => void\n labels?: Partial<CourseGuestCheckoutFormLabels>\n errorMessages?: Partial<Record<AcademyCheckoutErrorCode, string>>\n invoiceLabels?: Partial<FullInvoiceFieldsLabels>\n invoiceCountries?: ReadonlyArray<{ code: string; name: string }>\n currencyCode?: string\n locale?: string\n className?: string\n}\n\ntype FieldName = 'firstName' | 'lastName' | 'email' | 'terms' | 'consent'\ntype FieldErrors = Partial<Record<FieldName, string>>\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nconst FIELD_CLASS =\n 'w-full rounded-lg border bg-background px-3 py-2 text-sm transition focus:outline-none focus:ring-2 focus:ring-primary/20'\n\nasync function postToRoute(\n endpoint: string,\n input: CourseGuestCheckoutInput,\n): Promise<CourseGuestCheckoutResult> {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n const json = (await res.json().catch(() => null)) as {\n data?: AcademyCheckoutSession\n code?: unknown\n details?: unknown\n } | null\n if (res.ok && json?.data) return { ok: true, data: json.data }\n return {\n ok: false,\n status: res.status,\n ...(typeof json?.code === 'string' ? { code: json.code } : {}),\n ...(json?.details !== undefined ? { details: json.details } : {}),\n }\n } catch {\n return { ok: false, status: 0 }\n }\n}\n\n/** `details.fieldErrors` (one string per field) of a refusal, or `{}`. */\nfunction fieldErrorsOf(details: unknown): Record<string, string> {\n const errors = (details as { fieldErrors?: unknown } | null)?.fieldErrors\n if (typeof errors !== 'object' || errors === null) return {}\n return Object.fromEntries(\n Object.entries(errors).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n )\n}\n\nfunction toInvoiceErrors(fieldErrors: Record<string, string>): FullInvoiceErrors {\n const out: FullInvoiceErrors = {}\n for (const key of ['billingIdType', 'billingIdCountry', 'billingNif', 'billingName'] as const) {\n if (fieldErrors[key]) out[key] = fieldErrors[key]\n }\n if (fieldErrors.billingAddress) out.address = fieldErrors.billingAddress\n return out\n}\n\nexport function CourseGuestCheckoutForm({\n course,\n copy,\n termsHref,\n requestForm,\n issuesInvoices = true,\n simplifiedInvoiceCapCents = null,\n submit,\n endpoint = '/api/academy/checkout',\n onRedirect,\n onError,\n labels,\n errorMessages,\n invoiceLabels,\n invoiceCountries,\n currencyCode,\n locale,\n className,\n}: CourseGuestCheckoutFormProps) {\n const t: CourseGuestCheckoutFormLabels = {\n ...DEFAULT_COURSE_GUEST_CHECKOUT_FORM_LABELS,\n ...labels,\n }\n const messages = { ...DEFAULT_COURSE_GUEST_CHECKOUT_ERROR_MESSAGES, ...errorMessages }\n const id = useId()\n const [firstName, setFirstName] = useState('')\n const [lastName, setLastName] = useState('')\n const [email, setEmail] = useState('')\n const [acceptsTerms, setAcceptsTerms] = useState(false)\n const [acceptsConsent, setAcceptsConsent] = useState(false)\n const [invoice, setInvoice] = useState<FullInvoiceValue>(EMPTY_FULL_INVOICE_VALUE)\n const [errors, setErrors] = useState<FieldErrors>({})\n const [invoiceErrors, setInvoiceErrors] = useState<FullInvoiceErrors>({})\n const [formError, setFormError] = useState<string | null>(null)\n const [submitting, setSubmitting] = useState(false)\n const [needsRequest, setNeedsRequest] = useState(false)\n\n const consent = course.withdrawalConsent\n const hasTerms = Boolean(copy.termsVersion)\n const overCap = isOverSimplifiedInvoiceCap(course.priceCents, simplifiedInvoiceCapCents)\n const formattedTotal = formatPrice(course.priceCents, currencyCode, locale)\n const notice = copy.payNotice\n ? copy.payNotice.replaceAll('{total}', formattedTotal).replaceAll('{tenant}', copy.tenantName)\n : null\n\n if (needsRequest) {\n return (\n <div data-slot='course-guest-checkout-request' className={cn('space-y-4', className)}>\n {t.requestRequiredNotice ? (\n <p className='text-sm text-muted-foreground'>{t.requestRequiredNotice}</p>\n ) : null}\n {requestForm}\n </div>\n )\n }\n\n async function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault()\n if (submitting) return\n setFormError(null)\n setInvoiceErrors({})\n\n const next: FieldErrors = {}\n if (!firstName.trim()) next.firstName = t.firstNameRequired\n if (!lastName.trim()) next.lastName = t.lastNameRequired\n if (!EMAIL_PATTERN.test(email.trim())) next.email = t.emailInvalid\n if (hasTerms && !acceptsTerms) next.terms = t.termsRequired\n if (consent && !acceptsConsent) next.consent = t.consentRequired\n setErrors(next)\n if (Object.keys(next).length > 0) return\n\n const input: CourseGuestCheckoutInput = {\n courseSlug: course.slug,\n firstName: firstName.trim(),\n lastName: lastName.trim(),\n email: email.trim(),\n ...toCheckoutTermsFields(copy, acceptsTerms),\n ...(consent\n ? { acceptsWithdrawalWaiver: acceptsConsent, withdrawalWaiverVersion: consent.version }\n : {}),\n ...toAcademyCheckoutInvoiceFields(invoice, overCap, issuesInvoices),\n }\n\n setSubmitting(true)\n try {\n const result = await (submit ?? ((body) => postToRoute(endpoint, body)))(input).catch(\n (): CourseGuestCheckoutResult => ({ ok: false, status: 0 }),\n )\n if (result.ok) {\n ;(onRedirect ?? ((url: string) => window.location.assign(url)))(result.data.url)\n return\n }\n onError?.(result)\n if (result.code === 'ENROLLMENT_REQUEST_REQUIRED') {\n setNeedsRequest(true)\n return\n }\n if (result.code === 'INVALID_BILLING')\n setInvoiceErrors(toInvoiceErrors(fieldErrorsOf(result.details)))\n const code = result.code as AcademyCheckoutErrorCode | undefined\n setFormError((code && messages[code]) || t.genericError)\n } finally {\n setSubmitting(false)\n }\n }\n\n const fieldError = (field: FieldName) =>\n errors[field] ? (\n <p id={`${id}-${field}-error`} className='text-xs text-destructive'>\n {errors[field]}\n </p>\n ) : null\n const invalid = (field: FieldName) =>\n errors[field]\n ? { 'aria-invalid': true as const, 'aria-describedby': `${id}-${field}-error` }\n : {}\n const border = (field: FieldName) => (errors[field] ? 'border-destructive' : 'border-border')\n\n const textField = (\n field: 'firstName' | 'lastName' | 'email',\n label: string,\n value: string,\n onChange: (value: string) => void,\n autoComplete: string,\n ) => (\n <div className='space-y-1'>\n <label htmlFor={`${id}-${field}`} className='text-sm font-medium'>\n {label}\n </label>\n <input\n id={`${id}-${field}`}\n type={field === 'email' ? 'email' : 'text'}\n autoComplete={autoComplete}\n maxLength={field === 'email' ? 254 : 200}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n {...invalid(field)}\n className={cn(FIELD_CLASS, border(field))}\n />\n {fieldError(field)}\n </div>\n )\n\n return (\n <form\n noValidate\n onSubmit={handleSubmit}\n data-slot='course-guest-checkout-form'\n className={cn('space-y-6', className)}\n >\n <div className='grid grid-cols-1 gap-4 sm:grid-cols-2'>\n {textField('firstName', t.firstNameLabel, firstName, setFirstName, 'given-name')}\n {textField('lastName', t.lastNameLabel, lastName, setLastName, 'family-name')}\n <div className='sm:col-span-2'>\n {textField('email', t.emailLabel, email, setEmail, 'email')}\n </div>\n </div>\n\n <FullInvoiceFields\n value={invoice}\n onChange={setInvoice}\n overCap={overCap}\n issuesInvoices={issuesInvoices}\n errors={invoiceErrors}\n countries={invoiceCountries}\n labels={{\n ...(copy.fullInvoiceCheckboxLabel ? { toggle: copy.fullInvoiceCheckboxLabel } : {}),\n ...(copy.fullInvoiceHelpText ? { underCapNotice: copy.fullInvoiceHelpText } : {}),\n ...invoiceLabels,\n }}\n />\n\n <div className='space-y-3'>\n <CheckoutTermsAcceptance\n copy={copy}\n termsHref={termsHref}\n checked={acceptsTerms}\n onCheckedChange={setAcceptsTerms}\n error={errors.terms}\n id={`${id}-terms`}\n />\n {consent ? (\n <div\n data-slot='course-guest-checkout-consent'\n data-kind={consent.kind}\n className='space-y-1'\n >\n <div className='flex items-start gap-3'>\n <input\n id={`${id}-consent`}\n type='checkbox'\n checked={acceptsConsent}\n onChange={(e) => setAcceptsConsent(e.target.checked)}\n {...invalid('consent')}\n className='mt-0.5 size-5 shrink-0 cursor-pointer accent-primary'\n />\n <label htmlFor={`${id}-consent`} className='text-sm leading-relaxed'>\n {consent.text}\n </label>\n </div>\n {errors.consent ? (\n <p id={`${id}-consent-error`} className='pl-8 text-sm text-destructive'>\n {errors.consent}\n </p>\n ) : null}\n </div>\n ) : null}\n </div>\n\n <p data-slot='course-guest-checkout-total' className='text-base font-semibold'>\n {t.total(formattedTotal)}\n </p>\n\n {formError ? (\n <p role='alert' className='text-sm text-destructive'>\n {formError}\n </p>\n ) : null}\n\n <CheckoutPayAction\n label={copy.continueToPaymentLabel ?? t.submit}\n notice={notice}\n submitting={submitting}\n submittingLabel={t.submitting}\n id={`${id}-pay`}\n />\n </form>\n )\n}\n","import type {\n AcademyCheckoutBilling,\n AcademyCheckoutRequest,\n AcademyCourseDetail,\n} from '@reblu/site-contracts'\nimport type { FullInvoiceValue } from '../components/full-invoice-fields'\n\n/**\n * REB-1264 — pure, RSC-safe helpers of the guest course checkout\n * (`CourseGuestCheckoutForm`, `@reblu/site-blocks/client`). They ship from\n * the root entry so a Server Component can call them.\n */\n\n/**\n * Whether the course is bought in one step by a guest\n * (`POST /api/site/academy/checkout`): on sale, needing no validation and not\n * free. Otherwise keep the enrollment request flow. The server decides in the\n * end — it answers `ENROLLMENT_REQUEST_REQUIRED` when this is out of date.\n */\nexport function courseSellsAsGuest(\n course: Pick<AcademyCourseDetail, 'salesEnabled' | 'requiresValidation' | 'priceCents'>,\n): boolean {\n return course.salesEnabled && !course.requiresValidation && course.priceCents > 0\n}\n\n/**\n * The guest checkout body fields for the full-invoice request — the academy\n * twin of `toCheckoutInvoiceFields` (its `billingAddress` has no\n * first/last name). Empty when the buyer asks for no full invoice within the\n * cap, and always empty when the tenant issues no invoices.\n */\nexport function toAcademyCheckoutInvoiceFields(\n value: FullInvoiceValue,\n overCap: boolean,\n issuesInvoices = true,\n): Pick<AcademyCheckoutRequest, 'wantsFullInvoice' | 'billing'> {\n if (!issuesInvoices) return {}\n if (!value.wantsFullInvoice && !overCap) return {}\n const billing: AcademyCheckoutBilling = {\n billingName: value.billingName.trim(),\n billingNif: value.billingNif.trim(),\n billingIdType: value.billingIdType,\n ...(value.billingIdType === 'NIF' ? {} : { billingIdCountry: value.billingIdCountry }),\n billingAddress: {\n street: value.street.trim(),\n postalCode: value.postalCode.trim(),\n city: value.city.trim(),\n country: value.country,\n },\n }\n return { ...(value.wantsFullInvoice ? { wantsFullInvoice: true } : {}), billing }\n}\n","import { cn } from '@reblu/site-ui/utils'\n\n/**\n * `CheckoutPayAction` (REB-1198) — the last control of the single-step\n * checkout: the submit button (`ShopCheckoutCopy.continueToPaymentLabel`,\n * «Continuar al pago») and, right under it and readable (lawyer, 3-Nov: no\n * tiny text), the notice with the amount (`ShopQuote.payNotice`, «En el\n * siguiente paso pagarás {total} en la pasarela segura de Stripe…», already\n * filled). The button is described by the notice, so a screen reader hears\n * both. Place it inside the checkout `<form>`.\n */\nexport interface CheckoutPayActionProps {\n /** The tenant's button text: `ShopCheckoutCopy.continueToPaymentLabel`. */\n label: string\n /** The tenant's small print: `ShopQuote.payNotice`. Omit while there is no quote. */\n notice?: string | null\n submitting?: boolean\n /** Shown on the button while `submitting`. @default 'Procesando…' */\n submittingLabel?: string\n disabled?: boolean\n /** Prefix of the element ids (one action per page). @default 'checkout-pay' */\n id?: string\n className?: string\n /** Classes for the button; REPLACE the default look. */\n buttonClassName?: string\n /** Classes for the notice; REPLACE the default `text-sm text-muted-foreground`. */\n noticeClassName?: string\n}\n\nexport function CheckoutPayAction({\n label,\n notice,\n submitting = false,\n submittingLabel = 'Procesando…',\n disabled = false,\n id = 'checkout-pay',\n className,\n buttonClassName,\n noticeClassName,\n}: CheckoutPayActionProps) {\n const noticeId = `${id}-notice`\n const isDisabled = submitting || disabled\n return (\n <div data-slot='checkout-pay-action' className={cn('space-y-3', className)}>\n <button\n type='submit'\n disabled={isDisabled}\n aria-busy={submitting || undefined}\n aria-describedby={notice ? noticeId : undefined}\n className={\n buttonClassName ??\n cn(\n 'flex w-full items-center justify-center gap-2 rounded-xl px-4 py-3 text-base font-semibold transition-all',\n isDisabled\n ? 'cursor-not-allowed bg-muted text-muted-foreground'\n : 'cursor-pointer bg-primary text-primary-foreground hover:bg-primary/90',\n )\n }\n >\n {submitting ? submittingLabel : label}\n </button>\n {notice ? (\n <p id={noticeId} data-slot='checkout-pay-notice' className={noticeClassName ?? 'text-sm text-muted-foreground'}>\n {notice}\n </p>\n ) : null}\n </div>\n )\n}\n","import type { ShopCheckoutCopy, ShopCheckoutInput } from '@reblu/site-contracts'\nimport { cn } from '@reblu/site-ui/utils'\n\n/**\n * `CheckoutTermsAcceptance` (REB-1198) — the mandatory, unticked box «He leído\n * y acepto las {terms} de {tenant}» of the single-step checkout. Every text is\n * the tenant's (`ShopConfig.checkout`, edited in nexus › Configuración ›\n * Legal): `{terms}` becomes the link to the full document (text\n * `termsLinkText`, target `termsLinkUrl` or the SITE's own `termsHref`, a new\n * tab when `isTermsLinkNewTab`), `{tenant}` the site name. A label without\n * `{terms}` gets the link appended.\n *\n * Renders nothing while the tenant has published no terms\n * (`termsVersion: null`): there is nothing to accept and checkout does not\n * ask. Send {@link toCheckoutTermsFields} with the checkout body; the server\n * refuses without it (`TERMS_NOT_ACCEPTED`) or with an old version\n * (`TERMS_VERSION_CHANGED`: reload the config and ask again).\n */\nexport type CheckoutTermsCopy = Pick<\n ShopCheckoutCopy,\n 'tenantName' | 'termsCheckboxLabel' | 'termsLinkText' | 'termsLinkUrl' | 'isTermsLinkNewTab' | 'termsVersion'\n>\n\nexport interface CheckoutTermsAcceptanceProps {\n copy: CheckoutTermsCopy\n /** The SITE's route to its terms page, used when the tenant set no `termsLinkUrl`. */\n termsHref: string\n checked: boolean\n onCheckedChange: (checked: boolean) => void\n /** E.g. «Debes aceptar las condiciones para continuar.» (the SITE's or the API's text). */\n error?: string | null\n /** Appended inside the link when it opens a new tab (screen readers). @default '(se abre en una pestaña nueva)' */\n newTabHint?: string\n /** The checkbox id (one box per page). @default 'checkout-terms' */\n id?: string\n className?: string\n}\n\n/** Splits a label around `{terms}`; no placeholder = the link goes at the end. */\nfunction splitTermsLabel(label: string): [string, string] {\n const at = label.indexOf('{terms}')\n return at === -1 ? [`${label} `, ''] : [label.slice(0, at), label.slice(at + '{terms}'.length)]\n}\n\nexport function CheckoutTermsAcceptance({\n copy,\n termsHref,\n checked,\n onCheckedChange,\n error,\n newTabHint = '(se abre en una pestaña nueva)',\n id,\n className,\n}: CheckoutTermsAcceptanceProps) {\n if (!copy.termsVersion) return null\n const boxId = id ?? 'checkout-terms'\n const errorId = `${boxId}-error`\n const [before, after] = splitTermsLabel(copy.termsCheckboxLabel).map((part) =>\n part.replaceAll('{tenant}', copy.tenantName),\n )\n const newTab = copy.isTermsLinkNewTab\n return (\n <div data-slot='checkout-terms' className={cn('space-y-1', className)}>\n <div className='flex items-start gap-3'>\n <input\n id={boxId}\n type='checkbox'\n required\n checked={checked}\n onChange={(e) => onCheckedChange(e.target.checked)}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className='mt-0.5 size-5 shrink-0 cursor-pointer accent-primary'\n />\n <label htmlFor={boxId} className='text-sm leading-relaxed'>\n {before}\n <a\n href={copy.termsLinkUrl ?? termsHref}\n {...(newTab ? { target: '_blank', rel: 'noopener noreferrer' } : {})}\n className='font-medium underline underline-offset-2 hover:text-primary'\n >\n {copy.termsLinkText}\n {newTab ? (\n <>\n {' '}\n <span className='sr-only'>{newTabHint}</span>\n </>\n ) : null}\n </a>\n {after}\n </label>\n </div>\n {error ? (\n <p id={errorId} className='pl-8 text-sm text-destructive'>\n {error}\n </p>\n ) : null}\n </div>\n )\n}\n\n/**\n * The checkout body fields for the terms box: `acceptsTerms` and the version\n * shown (`termsVersionId`), or nothing when the tenant published no terms.\n */\nexport function toCheckoutTermsFields(\n copy: Pick<ShopCheckoutCopy, 'termsVersion'> | null | undefined,\n accepted: boolean,\n): Pick<ShopCheckoutInput, 'acceptsTerms' | 'termsVersionId'> {\n const version = copy?.termsVersion\n return version ? { acceptsTerms: accepted, termsVersionId: version.id } : {}\n}\n","import { cn } from '@reblu/site-ui/utils'\nimport type { ShopBillingInput, ShopCheckoutInput } from '@reblu/site-contracts'\n\n/**\n * `FullInvoiceFields` — the shop checkout's full-invoice request, drop-in\n * (REB-73). Fully controlled (`value`/`onChange`), no hooks, no fetch: the\n * SITE owns the state and sends `toCheckoutInvoiceFields(value, overCap)`\n * with its checkout body.\n *\n * The rule it presents (owner decision 15-Oct-2026): «Si quieren factura\n * completa, si o si deben aportar el NIF, si no lo aportan se emite factura\n * simplificada». The identifier is a Spanish NIF/NIE/CIF or, for a foreign\n * buyer, an EU VAT id, a passport or an ID document of their country — with\n * the country that issued it. Over the shop's simplified-invoice cap\n * (`ShopConfig.simplifiedInvoiceCapCents`, see `isOverSimplifiedInvoiceCap`)\n * a full invoice is the only legal one: pass `overCap` and the fields become\n * required, without a toggle. The server validates the identifier and\n * refuses with `BILLING_DATA_REQUIRED` what this form cannot prevent.\n *\n * A shop that issues no invoices (`ShopConfig.simplifiedInvoiceCapCents` is\n * `null`, see `shopIssuesInvoices`) offers no opt-in: pass\n * `issuesInvoices={false}` and the block renders nothing — the server would\n * refuse `wantsFullInvoice: true` with `FULL_INVOICE_UNAVAILABLE`.\n */\nexport type FullInvoiceIdType = NonNullable<ShopBillingInput['billingIdType']>\n\nexport interface FullInvoiceValue {\n wantsFullInvoice: boolean\n billingIdType: FullInvoiceIdType\n /** ISO 3166-1 alpha-2 country that issued a foreign document. */\n billingIdCountry: string\n billingNif: string\n billingName: string\n street: string\n postalCode: string\n city: string\n /** ISO 3166-1 alpha-2 country of the billing address. */\n country: string\n}\n\nexport const EMPTY_FULL_INVOICE_VALUE: FullInvoiceValue = {\n wantsFullInvoice: false,\n billingIdType: 'NIF',\n billingIdCountry: '',\n billingNif: '',\n billingName: '',\n street: '',\n postalCode: '',\n city: '',\n country: 'ES',\n}\n\nexport type FullInvoiceErrors = Partial<\n Record<'billingIdType' | 'billingIdCountry' | 'billingNif' | 'billingName' | 'address', string>\n>\n\nexport interface FullInvoiceFieldsLabels {\n toggle: string\n underCapNotice: string\n overCapNotice: string\n idType: string\n idCountry: string\n chooseCountry: string\n spanishNif: string\n documentNumber: string\n name: string\n street: string\n postalCode: string\n city: string\n country: string\n idTypeOptions: Record<FullInvoiceIdType, string>\n}\n\nconst DEFAULT_LABELS: FullInvoiceFieldsLabels = {\n toggle: '¿Quieres factura completa?',\n underCapNotice:\n 'Para una factura completa necesitas tu NIF/NIE o un documento de identificación. Si no, recibirás una factura simplificada.',\n overCapNotice:\n 'El importe del pedido supera el máximo de la factura simplificada: para completar la compra, indica tu NIF/NIE o un documento de identificación, tu nombre y tu dirección de facturación.',\n idType: 'Tipo de documento',\n idCountry: 'País que expidió el documento',\n chooseCountry: 'Elige un país',\n spanishNif: 'NIF / NIE / CIF',\n documentNumber: 'Número de documento',\n name: 'Nombre o razón social',\n street: 'Dirección',\n postalCode: 'Código postal',\n city: 'Ciudad',\n country: 'País',\n idTypeOptions: {\n NIF: 'NIF / NIE / CIF (España)',\n '02': 'NIF-IVA intracomunitario (otro país de la UE)',\n '03': 'Pasaporte',\n '04': 'Documento oficial de identidad de tu país de residencia',\n '05': 'Certificado de residencia',\n '06': 'Otro documento acreditativo',\n },\n}\n\nconst ID_TYPES: FullInvoiceIdType[] = ['NIF', '02', '03', '04', '05', '06']\n\n/** ISO 3166-1 alpha-2 countries offered by default: Spain, the EU and frequent others. */\nexport const DEFAULT_INVOICE_COUNTRIES: ReadonlyArray<{ code: string; name: string }> = [\n { code: 'ES', name: 'España' },\n { code: 'DE', name: 'Alemania' },\n { code: 'AD', name: 'Andorra' },\n { code: 'AR', name: 'Argentina' },\n { code: 'AT', name: 'Austria' },\n { code: 'BE', name: 'Bélgica' },\n { code: 'BG', name: 'Bulgaria' },\n { code: 'CY', name: 'Chipre' },\n { code: 'CO', name: 'Colombia' },\n { code: 'HR', name: 'Croacia' },\n { code: 'DK', name: 'Dinamarca' },\n { code: 'SK', name: 'Eslovaquia' },\n { code: 'SI', name: 'Eslovenia' },\n { code: 'US', name: 'Estados Unidos' },\n { code: 'EE', name: 'Estonia' },\n { code: 'FI', name: 'Finlandia' },\n { code: 'FR', name: 'Francia' },\n { code: 'GR', name: 'Grecia' },\n { code: 'HU', name: 'Hungría' },\n { code: 'IE', name: 'Irlanda' },\n { code: 'IT', name: 'Italia' },\n { code: 'LV', name: 'Letonia' },\n { code: 'LT', name: 'Lituania' },\n { code: 'LU', name: 'Luxemburgo' },\n { code: 'MT', name: 'Malta' },\n { code: 'MA', name: 'Marruecos' },\n { code: 'MX', name: 'México' },\n { code: 'NL', name: 'Países Bajos' },\n { code: 'PL', name: 'Polonia' },\n { code: 'PT', name: 'Portugal' },\n { code: 'GB', name: 'Reino Unido' },\n { code: 'CZ', name: 'República Checa' },\n { code: 'RO', name: 'Rumanía' },\n { code: 'SE', name: 'Suecia' },\n { code: 'CH', name: 'Suiza' },\n { code: 'VE', name: 'Venezuela' },\n]\n\n/**\n * Whether the shop issues invoices at all, read from the field the contract\n * defines for it: `ShopConfig.simplifiedInvoiceCapCents` is the effective cap\n * while invoicing is on and `null` otherwise (absent on an older base-api,\n * which accepts no opt-in either).\n */\nexport function shopIssuesInvoices(config: { simplifiedInvoiceCapCents?: number | null }): boolean {\n return config.simplifiedInvoiceCapCents != null\n}\n\n/** Whether a cart total (cents) is over the shop's simplified-invoice cap; `null` cap = no gate. */\nexport function isOverSimplifiedInvoiceCap(totalCents: number, capCents: number | null): boolean {\n return capCents != null && totalCents > capCents\n}\n\n/**\n * The checkout body fields for the request: `wantsFullInvoice` and the\n * `billing` block (identifier, its type and country, name and address). Empty\n * when the buyer asks for no full invoice within the cap, and always empty\n * when the shop issues no invoices (`issuesInvoices` false).\n */\nexport function toCheckoutInvoiceFields(\n value: FullInvoiceValue,\n overCap: boolean,\n issuesInvoices = true,\n): Pick<ShopCheckoutInput, 'wantsFullInvoice' | 'billing'> {\n if (!issuesInvoices) return {}\n if (!value.wantsFullInvoice && !overCap) return {}\n const name = value.billingName.trim()\n const [first = '', ...rest] = name.split(/\\s+/)\n const billing: ShopBillingInput = {\n billingName: name,\n billingNif: value.billingNif.trim(),\n billingIdType: value.billingIdType,\n ...(value.billingIdType === 'NIF' ? {} : { billingIdCountry: value.billingIdCountry }),\n billingAddress: {\n firstName: first,\n lastName: rest.join(' ') || first,\n street: value.street.trim(),\n city: value.city.trim(),\n postalCode: value.postalCode.trim(),\n country: value.country,\n },\n }\n return { ...(value.wantsFullInvoice ? { wantsFullInvoice: true } : {}), billing }\n}\n\nexport interface FullInvoiceFieldsProps {\n value: FullInvoiceValue\n onChange: (next: FullInvoiceValue) => void\n /** The cart total is over `ShopConfig.simplifiedInvoiceCapCents`. */\n overCap: boolean\n /**\n * `false` when the shop issues no invoices (`shopIssuesInvoices(config)`):\n * the block renders nothing. Defaults to `true`.\n */\n issuesInvoices?: boolean\n errors?: FullInvoiceErrors\n countries?: ReadonlyArray<{ code: string; name: string }>\n labels?: Partial<FullInvoiceFieldsLabels>\n className?: string\n}\n\nconst FIELD_CLASS =\n 'w-full rounded-lg border border-border bg-background px-3 py-2 text-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20'\n\nexport function FullInvoiceFields({\n value,\n onChange,\n overCap,\n issuesInvoices = true,\n errors = {},\n countries = DEFAULT_INVOICE_COUNTRIES,\n labels,\n className,\n}: FullInvoiceFieldsProps) {\n if (!issuesInvoices) return null\n const t = { ...DEFAULT_LABELS, ...labels }\n const showFields = value.wantsFullInvoice || overCap\n const isSpanishNif = value.billingIdType === 'NIF'\n const set = <K extends keyof FullInvoiceValue>(key: K, next: FullInvoiceValue[K]) =>\n onChange({ ...value, [key]: next })\n\n const error = (key: keyof FullInvoiceErrors) =>\n errors[key] ? (\n <p id={`full-invoice-${key}-error`} className='text-sm text-destructive'>\n {errors[key]}\n </p>\n ) : null\n const invalid = (key: keyof FullInvoiceErrors) =>\n errors[key]\n ? { 'aria-invalid': true as const, 'aria-describedby': `full-invoice-${key}-error` }\n : {}\n\n return (\n <section className={cn('space-y-4', className)} data-slot='full-invoice-fields'>\n {overCap ? (\n <p role='note' className='rounded-lg border border-border p-4 text-sm'>\n {t.overCapNotice}\n </p>\n ) : (\n <div className='space-y-1'>\n <label className='flex items-center gap-2 text-sm font-medium'>\n <input\n type='checkbox'\n checked={value.wantsFullInvoice}\n onChange={(e) => set('wantsFullInvoice', e.target.checked)}\n />\n {t.toggle}\n </label>\n <p className='text-sm text-muted-foreground'>{t.underCapNotice}</p>\n </div>\n )}\n\n {showFields && (\n <div className='grid grid-cols-1 gap-4 sm:grid-cols-2'>\n <div className='space-y-1'>\n <label htmlFor='full-invoice-id-type' className='text-sm font-medium'>\n {t.idType}\n </label>\n <select\n id='full-invoice-id-type'\n className={FIELD_CLASS}\n required\n value={value.billingIdType}\n onChange={(e) => set('billingIdType', e.target.value as FullInvoiceIdType)}\n {...invalid('billingIdType')}\n >\n {ID_TYPES.map((type) => (\n <option key={type} value={type}>\n {t.idTypeOptions[type]}\n </option>\n ))}\n </select>\n {error('billingIdType')}\n </div>\n\n {!isSpanishNif && (\n <div className='space-y-1'>\n <label htmlFor='full-invoice-id-country' className='text-sm font-medium'>\n {t.idCountry}\n </label>\n <select\n id='full-invoice-id-country'\n className={FIELD_CLASS}\n required\n value={value.billingIdCountry}\n onChange={(e) => set('billingIdCountry', e.target.value)}\n {...invalid('billingIdCountry')}\n >\n <option value='' disabled>\n {t.chooseCountry}\n </option>\n {countries.map((c) => (\n <option key={c.code} value={c.code}>\n {c.name}\n </option>\n ))}\n </select>\n {error('billingIdCountry')}\n </div>\n )}\n\n <div className='space-y-1'>\n <label htmlFor='full-invoice-nif' className='text-sm font-medium'>\n {isSpanishNif ? t.spanishNif : t.documentNumber}\n </label>\n <input\n id='full-invoice-nif'\n className={FIELD_CLASS}\n required\n value={value.billingNif}\n onChange={(e) => set('billingNif', e.target.value)}\n {...invalid('billingNif')}\n />\n {error('billingNif')}\n </div>\n\n <div className='space-y-1'>\n <label htmlFor='full-invoice-name' className='text-sm font-medium'>\n {t.name}\n </label>\n <input\n id='full-invoice-name'\n className={FIELD_CLASS}\n required\n value={value.billingName}\n onChange={(e) => set('billingName', e.target.value)}\n {...invalid('billingName')}\n />\n {error('billingName')}\n </div>\n\n <div className='space-y-1 sm:col-span-2'>\n <label htmlFor='full-invoice-street' className='text-sm font-medium'>\n {t.street}\n </label>\n <input\n id='full-invoice-street'\n className={FIELD_CLASS}\n required\n value={value.street}\n onChange={(e) => set('street', e.target.value)}\n {...invalid('address')}\n />\n </div>\n <div className='space-y-1'>\n <label htmlFor='full-invoice-postal-code' className='text-sm font-medium'>\n {t.postalCode}\n </label>\n <input\n id='full-invoice-postal-code'\n className={FIELD_CLASS}\n required\n value={value.postalCode}\n onChange={(e) => set('postalCode', e.target.value)}\n />\n </div>\n <div className='space-y-1'>\n <label htmlFor='full-invoice-city' className='text-sm font-medium'>\n {t.city}\n </label>\n <input\n id='full-invoice-city'\n className={FIELD_CLASS}\n required\n value={value.city}\n onChange={(e) => set('city', e.target.value)}\n />\n </div>\n <div className='space-y-1'>\n <label htmlFor='full-invoice-country' className='text-sm font-medium'>\n {t.country}\n </label>\n <select\n id='full-invoice-country'\n className={FIELD_CLASS}\n required\n value={value.country}\n onChange={(e) => set('country', e.target.value)}\n >\n {countries.map((c) => (\n <option key={c.code} value={c.code}>\n {c.name}\n </option>\n ))}\n </select>\n </div>\n <div className='sm:col-span-2'>{error('address')}</div>\n </div>\n )}\n </section>\n )\n}\n","'use client'\n\nimport type { ReactNode } from 'react'\nimport { cn } from '@reblu/site-ui/utils'\nimport type { Review } from '@reblu/site-contracts'\n\nimport { slotClassName } from '../lib/slot-class-name'\nimport type { HeadingLevel } from './_heading'\nimport { ReviewCard, usableReviews } from './_review-card'\nimport { ProductCarousel, type ProductCarouselLabels } from './product-carousel'\n\n/**\n * `ReviewsCarousel` (REB-1272) — the tenant's published reviews as a carousel:\n * the same cards as `Reviews` (stars, text, author, date, «Ver en <fuente>»),\n * on the package's scroll-snap track (`ProductCarousel`), so there is one\n * carousel behaviour to keep accessible:\n *\n * - the track is a labelled, focusable region (`aria-roledescription`\n * «carrusel»): swipe on touch, arrow keys once focused;\n * - previous/next buttons (from `sm` up) with accessible names scroll one\n * page; the one at the current end is disabled; none when everything fits;\n * - NO autoplay, ever: nothing moves unless the visitor moves it;\n * - instant scrolling under `prefers-reduced-motion: reduce`.\n *\n * Same data rules as `Reviews`: only checkable reviews render, and `null`,\n * `[]` or nothing usable renders NOTHING. `notice` is the tenant's text on how\n * its reviews are checked, shown below the track.\n *\n * A Client Component: import it from `@reblu/site-blocks/client`.\n */\nexport interface ReviewsCarouselLabels extends ProductCarouselLabels {}\n\nexport const DEFAULT_REVIEWS_CAROUSEL_LABELS: ReviewsCarouselLabels = {\n region: 'Reseñas',\n roleDescription: 'carrusel',\n previous: 'Reseña anterior',\n next: 'Reseña siguiente',\n}\n\nexport interface ReviewsCarouselProps {\n /** `client.reviews.list()`'s `data` as served — may be `null` / `undefined`. */\n reviews: readonly Review[] | null | undefined\n /** Visible heading; it also names the carousel. */\n heading?: string\n /** @default 2 */\n headingLevel?: HeadingLevel\n /** The tenant's notice on how the reviews are checked. Omitted: no notice. */\n notice?: ReactNode\n /** Link text for a review's original. @default (name) => `Ver en ${name}` */\n linkLabel?: (sourceName: string) => string\n /** Visually hidden text appended to each link. */\n newWindowLabel?: string\n /** Locale of the review date. @default 'es-ES' */\n locale?: string\n labels?: Partial<ReviewsCarouselLabels>\n className?: string\n /** Classes for the section heading; they REPLACE the default typography (REB-997). */\n headingClassName?: string\n /** Classes for each slide (`<li>`), e.g. a different card width. */\n itemClassName?: string\n /** Classes for each card. */\n cardClassName?: string\n /** Classes for the notice; they REPLACE the default typography. */\n noticeClassName?: string\n}\n\nexport function ReviewsCarousel({\n reviews,\n heading,\n headingLevel = 2,\n notice,\n linkLabel = (name) => `Ver en ${name}`,\n newWindowLabel = '(se abre en una pestaña nueva)',\n locale = 'es-ES',\n labels,\n className,\n headingClassName,\n itemClassName,\n cardClassName,\n noticeClassName,\n}: ReviewsCarouselProps) {\n const usable = usableReviews(reviews)\n if (usable.length === 0) return null\n\n const hasNotice = notice !== undefined && notice !== null && notice !== ''\n return (\n <div data-slot='reviews-carousel' className={cn('space-y-4', className)}>\n <ProductCarousel<Review>\n products={usable}\n heading={heading}\n headingLevel={headingLevel}\n headingClassName={headingClassName}\n labels={{ ...DEFAULT_REVIEWS_CAROUSEL_LABELS, ...labels }}\n itemClassName={itemClassName}\n renderProduct={(review) => (\n <ReviewCard\n review={review}\n linkLabel={linkLabel}\n newWindowLabel={newWindowLabel}\n locale={locale}\n className={cardClassName}\n />\n )}\n />\n {hasNotice ? (\n <p\n data-slot='reviews-notice'\n className={slotClassName('', 'text-sm text-muted-foreground', noticeClassName)}\n >\n {notice}\n </p>\n ) : null}\n </div>\n )\n}\n","import { cn } from '@reblu/site-ui/utils'\nimport type { Review } from '@reblu/site-contracts'\n\n/**\n * REB-1272 — the review card and its rules, shared by `Reviews` (grid, Server\n * Component) and `ReviewsCarousel` (client). No hooks, no state: RSC-safe.\n */\n\nfunction isHttps(value: unknown): value is string {\n if (typeof value !== 'string') return false\n try {\n const url = new URL(value)\n return url.protocol === 'https:' && url.hostname !== ''\n } catch {\n return false\n }\n}\n\nexport function isUsableReview(value: unknown): value is Review {\n if (typeof value !== 'object' || value === null) return false\n const review = value as Partial<Review>\n return (\n typeof review.id === 'string' &&\n typeof review.authorName === 'string' &&\n review.authorName.trim() !== '' &&\n typeof review.rating === 'number' &&\n Number.isInteger(review.rating) &&\n review.rating >= 1 &&\n review.rating <= 5 &&\n typeof review.sourceName === 'string' &&\n isHttps(review.sourceUrl)\n )\n}\n\nfunction formatDay(day: string, locale: string): string | null {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(day)) return null\n const date = new Date(`${day}T00:00:00.000Z`)\n if (Number.isNaN(date.getTime())) return null\n // UTC both ways: the stored value is a calendar day, never shifted by the\n // visitor's time zone.\n return new Intl.DateTimeFormat(locale, { dateStyle: 'long', timeZone: 'UTC' }).format(date)\n}\n\nconst STAR_PATH = 'M12 2.5l2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 17.3l-5.8 3.1 1.1-6.5L2.6 9.3l6.5-.9z'\n\nfunction Stars({ rating }: { rating: number }) {\n return (\n <div role='img' aria-label={`${rating} de 5 estrellas`} className='flex gap-0.5'>\n {[1, 2, 3, 4, 5].map((n) => (\n <svg\n key={n}\n aria-hidden='true'\n viewBox='0 0 24 24'\n width='16'\n height='16'\n data-filled={n <= rating ? 'true' : 'false'}\n className={n <= rating ? 'fill-current' : 'fill-none opacity-40'}\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinejoin='round'\n >\n <path d={STAR_PATH} />\n </svg>\n ))}\n </div>\n )\n}\n\n/** Only the reviews that can be checked: an https original, a 1..5 rating and an author. */\nexport function usableReviews(reviews: readonly Review[] | null | undefined): Review[] {\n return Array.isArray(reviews) ? (reviews as unknown[]).filter(isUsableReview) : []\n}\n\nexport interface ReviewCardProps {\n review: Review\n linkLabel: (sourceName: string) => string\n newWindowLabel: string\n locale: string\n className?: string\n}\n\n/** One review: stars, text, author, date and a link to the original. */\nexport function ReviewCard({\n review,\n linkLabel,\n newWindowLabel,\n locale,\n className,\n}: ReviewCardProps) {\n const date = formatDay(review.reviewDate, locale)\n return (\n <article\n data-slot='review'\n data-source={review.source}\n className={cn('flex h-full flex-col gap-3 rounded-lg border p-5', className)}\n >\n <Stars rating={review.rating} />\n {review.text && review.text.trim() !== '' ? (\n <blockquote className='flex-1'>\n <p>{review.text}</p>\n </blockquote>\n ) : null}\n <footer className='mt-auto space-y-1 text-sm'>\n <p className='font-semibold'>{review.authorName}</p>\n {date ? (\n <time dateTime={review.reviewDate} className='block text-muted-foreground'>\n {date}\n </time>\n ) : null}\n <a\n href={review.sourceUrl}\n target='_blank'\n rel='noopener nofollow'\n className='inline-flex underline underline-offset-4'\n >\n {linkLabel(review.sourceName)} <span className='sr-only'>{newWindowLabel}</span>\n </a>\n </footer>\n </article>\n )\n}\n","import type { ReactNode } from 'react'\nimport { cn } from '@reblu/site-ui/utils'\nimport type { Review } from '@reblu/site-contracts'\nimport { slotClassName } from '../lib/slot-class-name'\nimport { ReviewCard, usableReviews } from './_review-card'\nimport type { HeadingLevel } from './_heading'\n\n/**\n * `Reviews` — the tenant's published, verifiable reviews from\n * `client.reviews.list()` (REB-1272): a responsive grid of cards with the\n * star rating, the text, the author, the date and a «Ver en <fuente>» link to\n * the original review. Publishing, hiding or reordering in nexus changes the\n * SITE on the next revalidation, with no deploy.\n *\n * Server Component, RSC-safe: no hooks, no state, no `'use client'`.\n *\n * Semantics:\n * - `null`, `undefined`, a non-array or `[]` render NOTHING (no heading, no\n * notice): a SITE never shows an empty «Reseñas» section.\n * - A review is shown only if it can be checked: an `https://` link to the\n * original, a rating of 1 to 5 and an author. Anything else is skipped.\n * - A star-only review (`text: null`) renders without a quote.\n * - Stars are one `role=\"img\"` labelled «N de 5 estrellas»; the icons are\n * decorative.\n * - The link opens in a new tab with `rel=\"noopener nofollow\"` and a visually\n * hidden new-tab hint.\n * - `notice` is the tenant's short text on how reviews are checked (Spanish\n * consumer law asks a business showing reviews to say so), shown below the\n * grid, or above it with `noticePosition='above'`.\n * - `heading` wraps everything in a `<section>` labelled by it.\n */\n\nexport interface ReviewsProps {\n /** `client.reviews.list()`'s `data` as served — may be `null` / `undefined`. */\n reviews: readonly Review[] | null | undefined\n /** Optional section title, e.g. «Reseñas verificadas». */\n heading?: ReactNode\n /** `id` of the section heading (the section's `aria-labelledby`). @default 'reviews-heading' */\n headingId?: string\n /** Level of the section heading. @default 2 */\n headingLevel?: HeadingLevel\n /** The tenant's notice on how the reviews are checked. Omitted: no notice. */\n notice?: ReactNode\n /** Where the notice goes. @default 'below' */\n noticePosition?: 'above' | 'below'\n /** Link text for a review's original. @default (name) => `Ver en ${name}` */\n linkLabel?: (sourceName: string) => string\n /** Visually hidden text appended to each link. */\n newWindowLabel?: string\n /** Locale of the review date. @default 'es-ES' */\n locale?: string\n /** Classes for the root element. */\n className?: string\n /** Classes for the section heading; they REPLACE the default typography (REB-997). */\n headingClassName?: string\n /** Classes for the `<ul>` grid; they REPLACE the default columns. */\n gridClassName?: string\n /** Classes for each card. */\n cardClassName?: string\n /** Classes for the notice; they REPLACE the default typography. */\n noticeClassName?: string\n}\n\nexport function Reviews({\n reviews,\n heading,\n headingId = 'reviews-heading',\n headingLevel = 2,\n notice,\n noticePosition = 'below',\n linkLabel = (name) => `Ver en ${name}`,\n newWindowLabel = '(se abre en una pestaña nueva)',\n locale = 'es-ES',\n className,\n headingClassName,\n gridClassName,\n cardClassName,\n noticeClassName,\n}: ReviewsProps) {\n const usable = usableReviews(reviews)\n if (usable.length === 0) return null\n\n const hasNotice = notice !== undefined && notice !== null && notice !== ''\n const noticeNode = hasNotice ? (\n <p\n data-slot='reviews-notice'\n className={slotClassName('', 'text-sm text-muted-foreground', noticeClassName)}\n >\n {notice}\n </p>\n ) : null\n\n const grid = (\n <ul\n data-slot='reviews-grid'\n className={slotClassName('grid gap-6', 'sm:grid-cols-2 lg:grid-cols-3', gridClassName)}\n >\n {usable.map((review) => {\n return (\n <li key={review.id}>\n <ReviewCard\n review={review}\n linkLabel={linkLabel}\n newWindowLabel={newWindowLabel}\n locale={locale}\n className={cardClassName}\n />\n </li>\n )\n })}\n </ul>\n )\n\n const body = (\n <>\n {noticePosition === 'above' ? noticeNode : null}\n {grid}\n {noticePosition === 'below' ? noticeNode : null}\n </>\n )\n\n const hasHeading = heading !== undefined && heading !== null && heading !== ''\n if (!hasHeading) {\n return (\n <div data-slot='reviews' className={cn('space-y-4', className)}>\n {body}\n </div>\n )\n }\n\n const Tag = `h${headingLevel}` as const\n return (\n <section data-slot='reviews' aria-labelledby={headingId} className={cn('space-y-4', className)}>\n <Tag id={headingId} className={slotClassName('', 'text-xl font-semibold', headingClassName)}>\n {heading}\n </Tag>\n {body}\n </section>\n )\n}\n","'use client'\n\nimport type { ReactNode } from 'react'\nimport type { Review } from '@reblu/site-contracts'\n\nimport type { HeadingLevel } from './_heading'\nimport { Reviews } from './reviews'\nimport { ReviewsCarousel, type ReviewsCarouselLabels } from './reviews-carousel'\n\n/**\n * `FeaturedReviews` (REB-1272, owner 8-Nov) — the reviews a tenant featured\n * for its home, in the layout it chose in nexus: `grid` renders the `Reviews`\n * grid, `carousel` renders `ReviewsCarousel` (accessible, never autoplays).\n * Feed it `client.reviews.featured()`'s `data` as served. An unknown layout (an\n * API newer than this package) falls back to the grid, never to nothing.\n *\n * A Client Component (the carousel needs the browser): import it from\n * `@reblu/site-blocks/client` and pass it the data from a Server Component.\n */\nexport type FeaturedReviewsLayout = 'grid' | 'carousel'\n\nexport interface FeaturedReviewsProps {\n reviews: readonly Review[] | null | undefined\n layout: FeaturedReviewsLayout | null | undefined\n /** Section title; it also names the carousel. */\n heading?: string\n /** @default 2 */\n headingLevel?: HeadingLevel\n /** The tenant's notice on how the reviews are checked. Omitted: no notice. */\n notice?: ReactNode\n /** Link text for a review's original. @default (name) => `Ver en ${name}` */\n linkLabel?: (sourceName: string) => string\n /** Locale of the review date. @default 'es-ES' */\n locale?: string\n /** Carousel labels (region, previous, next). */\n carouselLabels?: Partial<ReviewsCarouselLabels>\n className?: string\n /** Classes for each card, in both layouts. */\n cardClassName?: string\n /** Grid only: REPLACES the default columns. */\n gridClassName?: string\n /** Carousel only: classes for each slide, e.g. a different card width. */\n itemClassName?: string\n}\n\nexport function FeaturedReviews({\n reviews,\n layout,\n heading,\n headingLevel,\n notice,\n linkLabel,\n locale,\n carouselLabels,\n className,\n cardClassName,\n gridClassName,\n itemClassName,\n}: FeaturedReviewsProps) {\n if (layout === 'carousel') {\n return (\n <ReviewsCarousel\n reviews={reviews}\n heading={heading}\n headingLevel={headingLevel}\n notice={notice}\n linkLabel={linkLabel}\n locale={locale}\n labels={carouselLabels}\n className={className}\n cardClassName={cardClassName}\n itemClassName={itemClassName}\n />\n )\n }\n return (\n <Reviews\n reviews={reviews}\n heading={heading}\n headingLevel={headingLevel}\n notice={notice}\n linkLabel={linkLabel}\n locale={locale}\n className={className}\n cardClassName={cardClassName}\n gridClassName={gridClassName}\n />\n )\n}\n","'use client'\n\nimport { FeaturedReviews } from './featured-reviews'\nimport type { BlockRendererProps } from './page-blocks'\n\n/**\n * `ReviewsBlockRenderer` (REB-1272) — the «Reseñas» page block body with the\n * layout the tenant chose: `carousel` renders `ReviewsCarousel`, anything else\n * the `Reviews` grid. Pass it to `PageBlocks` as `components.reviews`; the\n * section wrapper (heading, background, CTA) still comes from `PageBlocks`.\n *\n * Client entry only (`@reblu/site-blocks/client`): the carousel needs the\n * browser, and the RSC-safe `PageBlocks` cannot import it.\n */\nexport function ReviewsBlockRenderer({ block }: BlockRendererProps<'reviews'>) {\n const { layout, notice } = block.data\n return (\n <FeaturedReviews\n reviews={block.reviews}\n layout={layout ?? 'grid'}\n notice={notice && notice.trim() !== '' ? notice : undefined}\n // The section already is a region named by its heading: the track gets a\n // distinct name, so a screen reader never lists two «Reseñas» regions.\n carouselLabels={{ region: 'Carrusel de reseñas' }}\n />\n )\n}\n","'use client'\n\nimport { useState } from 'react'\nimport { cn } from '@reblu/site-ui/utils'\nimport { filterByLink, linkFilterOptions, linkKey, type PageLinkRef } from '../lib/page-links'\nimport { ProfileCard } from './profile-card'\n\n/**\n * REB-1100 — `TeamDirectory`: the team listing with filter chips, one per page\n * the members link to (their areas, picked in nexus), «Todos» first. Drop it\n * in over `client.pages.list({ type: 'team' })`: each item's `links` feed the\n * chips. Members from an API that predates `links` render as a plain listing\n * with no chips. Client-only (the selected chip).\n */\n\nexport interface TeamDirectoryMember {\n slug: string\n title: string\n coverImageUrl?: string | null\n category?: { name: string; slug: string } | null\n professional?: { role?: string | null } | null\n links?: PageLinkRef[] | null\n}\n\nexport interface TeamDirectoryLabels {\n /** The chip that shows every member. */\n all: string\n /** Accessible name of the chip group. */\n filter: string\n}\n\nexport const DEFAULT_TEAM_DIRECTORY_LABELS: TeamDirectoryLabels = {\n all: 'Todos',\n filter: 'Filtrar el equipo',\n}\n\nexport interface TeamDirectoryProps<T extends TeamDirectoryMember = TeamDirectoryMember> {\n members: T[]\n /** Builds a member's profile href. A function cannot cross from a Server\n * Component to this client one: there, use `memberHrefBase`. */\n memberHref?: (member: T) => string\n /** `${memberHrefBase}/${slug}`, e.g. `'/equipo'`; `''` gives `/${slug}`.\n * Ignored when `memberHref` is given. Omit both for non-linking cards. */\n memberHrefBase?: string\n /** Also show each member's links as chips on their card. */\n showLinksOnCards?: boolean\n labels?: Partial<TeamDirectoryLabels>\n className?: string\n}\n\nconst CHIP_CLASS =\n 'inline-flex cursor-pointer items-center rounded-full border px-3 py-1 text-sm font-medium transition-colors aria-pressed:bg-primary aria-pressed:text-primary-foreground aria-pressed:border-primary hover:bg-muted'\n\nexport function TeamDirectory<T extends TeamDirectoryMember>({\n members,\n memberHref,\n memberHrefBase,\n showLinksOnCards = false,\n labels,\n className,\n}: TeamDirectoryProps<T>) {\n const t = { ...DEFAULT_TEAM_DIRECTORY_LABELS, ...labels }\n const [selected, setSelected] = useState<string | null>(null)\n\n if (members.length === 0) return null\n\n const options = linkFilterOptions(members)\n const visible = filterByLink(members, selected)\n\n return (\n <div className={cn('space-y-6', className)} data-slot='team-directory'>\n {options.length > 0 && (\n <div role='group' aria-label={t.filter} className='flex flex-wrap gap-2'>\n <button\n type='button'\n className={CHIP_CLASS}\n aria-pressed={selected === null}\n onClick={() => setSelected(null)}\n >\n {t.all}\n </button>\n {options.map((option) => (\n <button\n key={option.key}\n type='button'\n className={CHIP_CLASS}\n aria-pressed={selected === option.key}\n onClick={() => setSelected(option.key)}\n >\n {option.label}\n </button>\n ))}\n </div>\n )}\n <ul className='grid list-none grid-cols-1 gap-6 p-0 sm:grid-cols-2 lg:grid-cols-3'>\n {visible.map((member) => (\n <li key={linkKey(member)}>\n <ProfileCard\n name={member.title}\n role={member.professional?.role ?? undefined}\n imageUrl={member.coverImageUrl ?? undefined}\n specialties={showLinksOnCards ? (member.links ?? []).map((l) => l.title) : []}\n href={\n memberHref\n ? memberHref(member)\n : memberHrefBase !== undefined\n ? `${memberHrefBase}/${member.slug}`\n : undefined\n }\n className='h-full'\n />\n </li>\n ))}\n </ul>\n </div>\n )\n}\n","/**\n * REB-1100 — pure helpers over a page's `links` (the pages it picked in nexus,\n * e.g. a team profile's areas). RSC-safe, no React.\n *\n * Structural types on purpose: any list item that carries `links` works, and\n * an item from an API that predates `links` (absent) simply has none.\n */\n\nexport interface PageLinkRef {\n slug: string\n title: string\n category?: { name: string; slug: string } | null\n}\n\nexport interface LinkFilterOption {\n /** Stable key: `{categorySlug}/{slug}` (a slug is unique per category). */\n key: string\n label: string\n}\n\nexport function linkKey(link: PageLinkRef): string {\n return `${link.category?.slug ?? ''}/${link.slug}`\n}\n\n/** Each distinct link across `items`, once, in first-seen order. */\nexport function linkFilterOptions(items: Array<{ links?: PageLinkRef[] | null }>): LinkFilterOption[] {\n const seen = new Map<string, LinkFilterOption>()\n for (const item of items) {\n for (const link of item.links ?? []) {\n const key = linkKey(link)\n if (!seen.has(key)) seen.set(key, { key, label: link.title })\n }\n }\n return [...seen.values()]\n}\n\n/** The items linked to `key`; every item when `key` is null. */\nexport function filterByLink<T extends { links?: PageLinkRef[] | null }>(\n items: T[],\n key: string | null,\n): T[] {\n if (key === null) return items\n return items.filter((item) => (item.links ?? []).some((link) => linkKey(link) === key))\n}\n","import type { ProfessionalField } from '@reblu/site-contracts'\nimport { cn } from '@reblu/site-ui/utils'\nimport { ProfessionalFields } from './professional-fields'\n\n/**\n * `ProfileCard` — presentational professional/team-member card (the clinic\n * \"doctor card\" from the mockups). There is no SITE API contract for staff yet,\n * so the prop shape is local and intentionally minimal. Token-driven, RSC-safe.\n */\nexport interface ProfileCardProps {\n name: string\n role?: string\n imageUrl?: string\n /** Short list of specialties / tags shown as chips. */\n specialties?: string[]\n bio?: string\n /**\n * REB-1099 — the tenant's professional fields (`professional.fields` from\n * `client.pages.list`). Only the `showOnCard` ones are shown.\n */\n fields?: ProfessionalField[] | null\n /** Link target for the full profile. Omit to render non-linking. */\n href?: string\n className?: string\n}\n\n/** Up to two initials from the name, for the avatar fallback. */\nfunction initials(name: string): string {\n return name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((part) => part[0]?.toUpperCase() ?? '')\n .join('')\n}\n\nexport function ProfileCard({\n name,\n role,\n imageUrl,\n specialties = [],\n bio,\n fields,\n href,\n className,\n}: ProfileCardProps) {\n const NameTag = href ? 'a' : 'span'\n\n return (\n <article\n className={cn(\n 'flex flex-col items-center gap-3 rounded-lg border bg-card p-6 text-center',\n className,\n )}\n data-slot='profile-card'\n >\n <div className='relative h-24 w-24 overflow-hidden rounded-full bg-muted'>\n {imageUrl ? (\n <img src={imageUrl} alt={name} loading='lazy' className='h-full w-full object-cover' />\n ) : (\n <span\n className='flex h-full w-full items-center justify-center text-2xl font-semibold text-muted-foreground'\n aria-hidden='true'\n >\n {initials(name)}\n </span>\n )}\n </div>\n\n <div className='space-y-1'>\n <h3 className='text-lg font-semibold leading-tight'>\n <NameTag {...(href ? { href, className: 'transition-colors hover:text-primary' } : {})}>\n {name}\n </NameTag>\n </h3>\n {role && <p className='text-sm text-muted-foreground'>{role}</p>}\n </div>\n\n <ProfessionalFields fields={fields} variant='card' />\n\n {specialties.length > 0 && (\n <ul className='flex flex-wrap justify-center gap-1.5 p-0'>\n {specialties.map((s) => (\n <li\n key={s}\n className='inline-flex items-center rounded-full bg-secondary px-2.5 py-0.5 text-xs font-medium text-secondary-foreground'\n >\n {s}\n </li>\n ))}\n </ul>\n )}\n\n {bio && <p className='text-sm text-muted-foreground'>{bio}</p>}\n </article>\n )\n}\n","import type { ProfessionalField } from '@reblu/site-contracts'\nimport { cn } from '@reblu/site-ui/utils'\n\n/**\n * `ProfessionalFields` — the tenant-defined professional fields of a team\n * profile (REB-1099), e.g. a registration number, as a `<dl>`.\n *\n * Feed it `professional.fields` from the SITE API as-is: the list card\n * (`client.pages.list`) already carries only the card fields, the profile\n * (`client.pages.bySlug`) carries every public one. `variant='card'` filters\n * to `showOnCard` anyway, so one list can feed both places. The labels are the\n * tenant's own; nothing here names a field. Renders nothing when there is\n * nothing to show. RSC-safe.\n */\nexport interface ProfessionalFieldsProps {\n fields: ProfessionalField[] | null | undefined\n /** `'card'` keeps only `showOnCard` fields. Default `'profile'`: every field. */\n variant?: 'card' | 'profile'\n className?: string\n}\n\nexport function ProfessionalFields({\n fields,\n variant = 'profile',\n className,\n}: ProfessionalFieldsProps) {\n const shown = (fields ?? []).filter((f) => variant !== 'card' || f.showOnCard)\n if (shown.length === 0) return null\n\n return (\n <dl\n className={cn(\n 'grid gap-x-3 gap-y-1 text-sm',\n variant === 'card' ? 'justify-center' : 'grid-cols-[auto_1fr]',\n className,\n )}\n data-slot='professional-fields'\n data-variant={variant}\n >\n {shown.map((field, index) => (\n <div\n key={`${field.label}-${index}`}\n className={variant === 'card' ? 'flex justify-center gap-1' : 'contents'}\n >\n <dt className='text-muted-foreground'>{field.label}</dt>\n <dd className='m-0 font-medium'>{field.value}</dd>\n </div>\n ))}\n </dl>\n )\n}\n"],"mappings":"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","names":["import_utils","import_jsx_runtime","DEFAULT_LABELS","import_react","import_utils","import_jsx_runtime","DEFAULT_LABELS","import_react","import_utils","import_jsx_runtime","DEFAULT_LABELS","count","import_react","import_utils","import_jsx_runtime","import_utils","import_jsx_runtime","import_jsx_runtime","import_react","import_utils","import_utils","import_jsx_runtime","module","import_react","import_utils","import_jsx_runtime","import_react","import_utils","import_jsx_runtime","FIELD_ORDER","EMAIL_PATTERN","postToRoute","rejectionMessage","FIELD_CLASS","import_react","import_utils","import_utils","import_jsx_runtime","import_utils","import_jsx_runtime","import_utils","import_jsx_runtime","DEFAULT_LABELS","FIELD_CLASS","DEFAULT_LABELS","import_jsx_runtime","EMAIL_PATTERN","FIELD_CLASS","postToRoute","import_utils","import_utils","import_jsx_runtime","import_jsx_runtime","import_utils","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_utils","import_utils","import_utils","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}