'use client' /** * ConsumerButton — consumer-only actions (Figma-aligned). * * **When to use** * - Any button, submit, or link-styled action in `app/(consumer)/**`, * `components/consumer/**`, and shared consumer overlays (auth, PWA, booking). * * **When NOT to use** * - Admin/dashboard and event-create wizard → `components/formElements/Button`. * - Dual cancel/primary rows → `ConsumerActionButtons` (do not hand-roll pairs). * * **Props (design)** * - `fill`: `orange` | `navy` | `fourth` | `fifth` | `gray` | `none` (default `orange`) * - `size`: `xs` | `sm` | `md` | `lg` | `xl` — default `lg` text, `sm` icon-only * - `radius`: `full` | `control` (10px) * - `textColor`: `white`, Tailwind `text-*`, or hex — default white on filled * - `fontSize`: default 14 * - `align`: `center` | `start` (RTL: start = right) * - Icons: `iconStart` / `iconEnd` / `icon` + `iconPosition` * - `isLoading` disables the control and swaps the icon slot for a spinner (label stays) * * **هم‌نام بودن ≠ هم‌اندازه بودن:** `size` here ≠ `ConsumerInput` `size` for * the same token (see `frontend/docs/consumer-ui-guidelines.md` §4). * * **className policy:** layout-only (`flex-1`, `mt-5`, `w-full`, …) is OK. * Do **not** pass visual overrides (`bg-*`, `text-*`, `rounded-*`, `min-h-*`) * to match Figma — stop, tell the product owner, and extend this component * after explicit approval. * * Guidelines: `frontend/docs/consumer-ui-guidelines.md` */ import Link from 'next/link' import { Spinner } from '@heroui/react' import { forwardRef } from 'react' import { buildConsumerButtonStyle } from '@/components/consumer/consumerButtonStyles' import type { ConsumerButtonProps } from '@/components/consumer/consumerButtonTypes' export type { ConsumerButtonProps } from '@/components/consumer/consumerButtonTypes' const ConsumerButton = forwardRef( ( { 'aria-controls': ariaControls, 'aria-expanded': ariaExpanded, 'aria-label': ariaLabel, 'aria-pressed': ariaPressed, 'aria-checked': ariaChecked, 'aria-selected': ariaSelected, align = 'center', children, className, radius = 'full', disabled = false, fill = 'orange', fontSize = 14, fullWidth = false, icon, iconEnd, iconOnly = false, iconPosition = 'start', iconStart, isLoading = false, role, size, target = '_self', textColor, to, type = 'button', onClick, }, ref ) => { const isDisabled = disabled || isLoading const resolvedIconStart = iconStart ?? (iconPosition === 'start' ? icon : undefined) const resolvedIconEnd = iconEnd ?? (iconPosition === 'end' ? icon : undefined) const { className: resolvedClassName, style } = buildConsumerButtonStyle({ align, className, radius, fill, fontSize, fullWidth, iconOnly, size, textColor, }) const iconContent = resolvedIconStart ?? (iconOnly ? children : undefined) const loadingSpinner = ( ) // لودینگ: فقط اسلات آیکن با اسپینر عوض شود؛ لیبل باقی بماند const showStartSpinner = isLoading && Boolean(resolvedIconStart || !resolvedIconEnd) const showEndSpinner = isLoading && !resolvedIconStart && Boolean(resolvedIconEnd) const content = iconOnly ? ( isLoading ? ( loadingSpinner ) : ( iconContent ) ) : ( <> {showStartSpinner ? loadingSpinner : resolvedIconStart} {children} {showEndSpinner ? loadingSpinner : isLoading ? null : resolvedIconEnd} ) const ariaProps = { 'aria-checked': ariaChecked, 'aria-controls': ariaControls, 'aria-expanded': ariaExpanded, 'aria-label': ariaLabel, 'aria-pressed': ariaPressed, 'aria-selected': ariaSelected, role, } if (to) { return ( { event.preventDefault() } : onClick } > {content} ) } return ( ) } ) ConsumerButton.displayName = 'ConsumerButton' export default ConsumerButton