All files / src/components/ui/links ProductLink.tsx

89.25% Statements 108/121
100% Branches 1/1
0% Functions 0/1
89.25% Lines 108/121

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 1221x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x                           30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 1x 1x 1x  
'use client';
 
import { AnchorHTMLAttributes, forwardRef } from 'react';
import Link from 'next/link';
import { useDispatch } from 'react-redux';
import { AppDispatch } from '@/redux/store';
import { updateProductDetails } from '@/redux/features/productDetails';
import { eventTracking } from '@/lib/event-tracking';
import { cn } from '@/lib/core';
 
/**
 * ProductLink Component Props
 */
export interface ProductLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'onClick'> {
  /** Product data (at minimum, id and title) */
  product: { id: number; title: string };
  /** Children (link content) */
  children: React.ReactNode;
  /** Additional onClick handler (called after built-in handlers) */
  onClick?: () => void;
  /** Disable Redux state update */
  disableStateUpdate?: boolean;
  /** Disable event tracking */
  disableTracking?: boolean;
}
 
/**
 * ProductLink - Centralized product link component
 *
 * Features:
 * - Automatic URL routing to product details page
 * - Built-in Redux state updates for product details
 * - Automatic event tracking
 * - Consistent styling (hover effects)
 * - Type-safe with product data
 *
 * Benefits:
 * - Single place to change product URL structure
 * - Consistent behavior across all product links
 * - Automatic analytics tracking
 * - No need to manually dispatch Redux actions
 *
 * @example
 * ```tsx
 * // Basic usage
 * <ProductLink product={product}>
 *   {product.title}
 * </ProductLink>
 *
 * // With custom styling
 * <ProductLink
 *   product={product}
 *   className="font-bold text-lg"
 * >
 *   {product.title}
 * </ProductLink>
 *
 * // With additional onClick handler
 * <ProductLink
 *   product={product}
 *   onClick={() => console.log('Clicked!')}
 * >
 *   View Details
 * </ProductLink>
 *
 * // Disable state/tracking (for special cases)
 * <ProductLink
 *   product={product}
 *   disableStateUpdate
 *   disableTracking
 * >
 *   {product.title}
 * </ProductLink>
 * ```
 */
export const ProductLink = forwardRef<HTMLAnchorElement, ProductLinkProps>(
  (
    {
      product,
      children,
      onClick,
      disableStateUpdate = false,
      disableTracking = false,
      className,
      ...props
    },
    ref
  ) => {
    const dispatch = useDispatch<AppDispatch>();
 
    const handleClick = () => {
      // Update Redux state
      if (!disableStateUpdate) {
        // Spread to create plain object compatible with Redux payload type
        dispatch(updateProductDetails({ ...product }));
      }

      // Track event
      if (!disableTracking) {
        eventTracking.productView(product.id, product.title);
      }

      // Call additional onClick handler
      onClick?.();
    };
 
    return (
      <Link
        ref={ref}
        href={`/product/${product.id}`}
        onClick={handleClick}
        className={cn('hover:text-blue transition-colors duration-200', className)}
        {...props}
      >
        {children}
      </Link>
    );
  }
);
 
ProductLink.displayName = 'ProductLink';