import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useParams, Link } from 'react-router-dom' import { Clock, Users, ChefHat, ArrowLeft, Printer, Star, AlertTriangle, MessageSquare } from 'lucide-react' import { mealPlannerApi } from '../api' import type { MealPlanItem, Feedback } from '../types' import { Button } from '../components/ui/Button' import { Badge } from '../components/ui/Badge' import { Card, CardBody, CardHeader } from '../components/ui/Card' import { Skeleton, SkeletonText } from '../components/ui/Skeleton' import { Select } from '../components/ui/Select' import { Textarea } from '../components/ui/Textarea' import { showToast } from '../lib/toast' const DENIAL_REASONS = [ { value: '', label: 'Select a reason...' }, { value: 'too_expensive', label: 'Too expensive' }, { value: 'boring', label: 'Boring / not interesting' }, { value: 'disliked_ingredient', label: 'Disliked ingredient' }, { value: 'cultural', label: 'Cultural / dietary preference' }, { value: 'other', label: 'Other' }, ] function StarRating({ value, onChange }: { value: number; onChange: (n: number) => void }) { return (
{[1, 2, 3, 4, 5].map((n) => ( ))}
) } function SavedRating({ rating }: { rating: number }) { return (
{[1, 2, 3, 4, 5].map((n) => ( ))}
) } function MealDetailSkeleton() { return (
) } export default function MealDetail() { const { id } = useParams<{ id: string }>() const queryClient = useQueryClient() const { data: item, isLoading } = useQuery({ queryKey: ['mealItem', id], queryFn: () => mealPlannerApi.meals.getItem(id!).then(r => r.data), enabled: !!id, }) const { data: existingFeedback } = useQuery({ queryKey: ['feedback', id], queryFn: () => mealPlannerApi.feedback.get(id!).then(r => r.data), enabled: !!id, }) const [rating, setRating] = useState(0) const [neverSuggest, setNeverSuggest] = useState(false) const [reason, setReason] = useState('') const [text, setText] = useState('') const [submitted, setSubmitted] = useState(false) const [editingFeedback, setEditingFeedback] = useState(false) const submitMutation = useMutation({ mutationFn: (payload: any) => mealPlannerApi.feedback.create(payload), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['feedback', id] }) setSubmitted(true) setEditingFeedback(false) showToast.success('Feedback saved!') }, onError: () => { showToast.error('Failed to save feedback. Please try again.') }, }) if (isLoading) { return } if (!item?.recipe) { return (
Back to meal plan

Recipe Not Found

This meal item doesn't have an associated recipe.

) } const recipe = item.recipe const feedback = existingFeedback const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (!id) return submitMutation.mutate({ meal_plan_item_id: id, rating: rating || null, never_suggest: neverSuggest, denial_reason: reason || null, feedback_text: text || null, }) } const totalTime = recipe.total_time_minutes ?? (recipe.prep_time_minutes || 0) + (recipe.cook_time_minutes || 0) return (
{/* Back Link */} Back to meal plan {/* Hero */}
{recipe.image_url ? ( {recipe.name} ) : (
)}

{recipe.name}

{recipe.description && (

{recipe.description}

)}
${item.estimated_cost?.toFixed(2) || 'N/A'}
per serving
{/* Metadata Row */}
{totalTime > 0 && (
{totalTime} min total
)} {recipe.prep_time_minutes != null && recipe.prep_time_minutes > 0 && (
Prep {recipe.prep_time_minutes} min
)} {recipe.cook_time_minutes != null && recipe.cook_time_minutes > 0 && (
Cook {recipe.cook_time_minutes} min
)}
{recipe.servings} servings
{recipe.cuisine_tags?.length > 0 && (
{recipe.cuisine_tags.map(tag => ( {tag} ))}
)} {recipe.dietary_tags?.length > 0 && (
{recipe.dietary_tags.map(tag => ( {tag} ))}
)}
{/* Ingredients */}

Ingredients

    {recipe.ingredients?.map((ing, idx) => (
  • {ing.quantity && `${ing.quantity} `} {ing.unit && `${ing.unit} `} {ing.name} {ing.is_optional && ( (optional) )}
  • ))}
{/* Instructions */}

Instructions

    {recipe.instructions?.map((step, idx) => (
  1. {idx + 1}

    {step}

  2. ))}
{/* Feedback */}

Feedback

{feedback?.rating && !editingFeedback ? (
Your rating:
{feedback.never_suggest && ( Never suggest this recipe again )} {feedback.denial_reason && (

Reason:{' '} {feedback.denial_reason.replace(/_/g, ' ')}

)} {feedback.feedback_text && (
"{feedback.feedback_text}"
)}
) : submitted && !editingFeedback ? (

Thanks for your feedback!

) : (
setNeverSuggest(e.target.checked)} className="w-4 h-4 rounded border-danger-300 text-danger-600 focus:ring-danger-500" />