All files / src/components RecipeCard.tsx

0% Statements 0/14
0% Branches 0/27
0% Functions 0/4
0% Lines 0/12

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                                                                                                                                                                                                                                 
import { Star, Clock, Heart } from 'lucide-react'
import type { Recipe } from '../api/client'
import { cn } from '../lib/utils'
 
interface RecipeCardProps {
  recipe: Recipe
  isFavorite?: boolean
  onFavoriteToggle?: (recipe: Recipe) => void
  onClick?: (recipe: Recipe) => void
}
 
export default function RecipeCard({
  recipe,
  isFavorite = false,
  onFavoriteToggle,
  onClick,
}: RecipeCardProps) {
  const imageUrl = recipe.image || recipe.image_url
 
  const formatTime = (minutes: number | null) => {
    if (!minutes) return null
    if (minutes < 60) return `${minutes} min`
    const hours = Math.floor(minutes / 60)
    const mins = minutes % 60
    return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`
  }
 
  const handleFavoriteClick = (e: React.MouseEvent) => {
    e.stopPropagation()
    onFavoriteToggle?.(recipe)
  }
 
  return (
    <div
      className={cn(
        'group relative overflow-hidden rounded-lg bg-card shadow-sm transition-all hover:shadow-md',
        onClick && 'cursor-pointer'
      )}
      onClick={() => onClick?.(recipe)}
    >
      {/* Image */}
      <div className="relative aspect-[4/3] overflow-hidden bg-muted">
        {imageUrl ? (
          <img
            src={imageUrl}
            alt={recipe.title}
            className="h-full w-full object-cover transition-transform group-hover:scale-105"
          />
        ) : (
          <div className="flex h-full w-full items-center justify-center text-muted-foreground">
            No image
          </div>
        )}
 
        {/* Favorite button */}
        {onFavoriteToggle && (
          <button
            onClick={handleFavoriteClick}
            className={cn(
              'absolute right-2 top-2 rounded-full bg-background/80 p-2 backdrop-blur-sm transition-colors',
              isFavorite
                ? 'text-accent hover:bg-background'
                : 'text-muted-foreground hover:bg-background hover:text-accent'
            )}
            aria-label={isFavorite ? 'Remove from favorites' : 'Add to favorites'}
          >
            <Heart
              className={cn('h-5 w-5', isFavorite && 'fill-current')}
            />
          </button>
        )}
 
        {/* Remix badge */}
        {recipe.is_remix && (
          <div className="absolute left-2 top-2 rounded-full bg-primary/90 px-2 py-0.5 text-xs text-primary-foreground backdrop-blur-sm">
            Remix
          </div>
        )}
      </div>
 
      {/* Content */}
      <div className="p-3">
        {/* Title */}
        <h3 className="mb-1 line-clamp-2 text-sm font-medium text-card-foreground">
          {recipe.title}
        </h3>
 
        {/* Meta */}
        <div className="flex items-center gap-3 text-xs text-muted-foreground">
          {/* Source */}
          <span className="truncate">{recipe.host}</span>
 
          {/* Time */}
          {recipe.total_time && (
            <span className="flex shrink-0 items-center gap-1">
              <Clock className="h-3 w-3" />
              {formatTime(recipe.total_time)}
            </span>
          )}
 
          {/* Rating */}
          {recipe.rating && (
            <span className="flex shrink-0 items-center gap-1">
              <Star className="h-3 w-3 fill-star text-star" />
              {recipe.rating.toFixed(1)}
            </span>
          )}
        </div>
      </div>
    </div>
  )
}
 
← Back to Dashboard