fridge-vision / frontend /src /components /RecipeCard.jsx
NoNameWrath's picture
Deploy to HuggingFace Spaces
c02c6ce
Raw
History Blame Contribute Delete
5.75 kB
import React, { useState } from 'react'
const DIFFICULTY = {
easy: { label: 'Easy', bg: '#F0FDF4', color: '#166534' },
medium: { label: 'Medium', bg: '#FFFBEB', color: '#92400E' },
hard: { label: 'Hard', bg: '#FFF5F5', color: '#991B1B' },
}
function pill(ing) {
return (typeof ing === 'string' ? ing : ing?.name || '').replace(/_/g, ' ')
}
export default function RecipeCard({ recipe, rank }) {
const [open, setOpen] = useState(false)
const pct = Math.round(recipe.match_percentage || 0)
const diff = DIFFICULTY[(recipe.difficulty || 'medium').toLowerCase()] || DIFFICULTY.medium
const barColor = pct >= 75 ? '#4ADE80' : pct >= 50 ? '#FCD34D' : '#D1D5DB'
return (
<div
className="rounded-xl border overflow-hidden"
style={{ backgroundColor: '#fff', borderColor: '#E8E4DC' }}
>
{/* ── Main row (always visible) ── */}
<button
onClick={() => setOpen(v => !v)}
className="w-full text-left flex items-center gap-3 px-4 py-3 transition-colors hover:bg-stone-50"
>
{/* Rank */}
<span className="text-xs font-black w-5 shrink-0 text-center" style={{ color: '#C8C3BB' }}>
{rank}
</span>
{/* Name + meta */}
<div className="flex-1 min-w-0">
<p className="font-bold text-sm leading-snug truncate" style={{ color: '#1C1B1A' }}>
{recipe.name}
</p>
<div className="flex items-center gap-2 mt-0.5">
<span
className="text-xs font-bold px-1.5 py-px rounded"
style={{ backgroundColor: diff.bg, color: diff.color }}
>
{diff.label}
</span>
{recipe.prep_time_mins && (
<span className="text-xs" style={{ color: '#9CA3AF' }}>
{recipe.prep_time_mins} min
</span>
)}
{recipe.servings && (
<span className="text-xs" style={{ color: '#9CA3AF' }}>
· {recipe.servings} srv
</span>
)}
</div>
</div>
{/* Match % + bar */}
<div className="shrink-0 flex flex-col items-end gap-1 w-14">
<span className="text-sm font-black" style={{ color: '#1C1B1A' }}>{pct}%</span>
<div className="w-full h-1 rounded-full overflow-hidden" style={{ backgroundColor: '#F0EDE6' }}>
<div
className="h-full rounded-full"
style={{ width: `${pct}%`, backgroundColor: barColor }}
/>
</div>
</div>
{/* Chevron */}
<span className="text-xs shrink-0" style={{ color: '#C8C3BB' }}>
{open ? '▲' : '▼'}
</span>
</button>
{/* ── Expanded detail ── */}
{open && (
<div className="px-4 pb-4 pt-1 border-t space-y-3" style={{ borderColor: '#F0EDE6' }}>
{/* Ingredient pills */}
<div className="flex flex-wrap gap-1.5">
{recipe.matched_ingredients?.map((ing, i) => (
<span
key={i}
className="text-xs font-medium px-2 py-0.5 rounded-full border capitalize"
style={{ backgroundColor: '#F0FDF4', borderColor: '#BBF7D0', color: '#166534' }}
>
{pill(ing)}
</span>
))}
{recipe.missing_ingredients?.map((ing, i) => (
<span
key={i}
className="text-xs font-medium px-2 py-0.5 rounded-full border capitalize"
style={{ backgroundColor: '#FAFAF8', borderColor: '#E8E4DC', color: '#9CA3AF' }}
>
+ {pill(ing)}
</span>
))}
</div>
{/* Description */}
{recipe.description && (
<p className="text-xs leading-relaxed" style={{ color: '#6B6560' }}>
{recipe.description}
</p>
)}
{/* External links */}
<div className="flex gap-2 pt-1">
<a
href={`https://www.youtube.com/results?search_query=how+to+make+${encodeURIComponent(recipe.name)}`}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 rounded-lg transition-opacity hover:opacity-80"
style={{ backgroundColor: '#FF0000', color: '#fff' }}
onClick={e => e.stopPropagation()}
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
<path d="M23.5 6.2a3 3 0 0 0-2.1-2.1C19.5 3.5 12 3.5 12 3.5s-7.5 0-9.4.6A3 3 0 0 0 .5 6.2 31 31 0 0 0 0 12a31 31 0 0 0 .5 5.8 3 3 0 0 0 2.1 2.1c1.9.6 9.4.6 9.4.6s7.5 0 9.4-.6a3 3 0 0 0 2.1-2.1A31 31 0 0 0 24 12a31 31 0 0 0-.5-5.8zM9.75 15.5v-7l6.5 3.5-6.5 3.5z"/>
</svg>
Watch on YouTube
</a>
<a
href={`https://www.google.com/search?q=${encodeURIComponent(recipe.name + ' recipe')}`}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 rounded-lg transition-opacity hover:opacity-80"
style={{ backgroundColor: '#F0EDE6', color: '#1C1B1A' }}
onClick={e => e.stopPropagation()}
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
Find Recipe
</a>
</div>
</div>
)}
</div>
)
}