import { useState, useMemo, useCallback } from "react"; import Image from "next/image"; import { Play, Shuffle, Trash2, GripVertical, Loader2 } from "lucide-react"; import { CSS } from "@dnd-kit/utilities"; import { useSortable } from "@dnd-kit/sortable"; import { useCurrentLocale, useI18n } from "locales/client"; import { InlineTooltip } from "@/components/ui/tooltip"; import { Button } from "@/components/ui/button"; import { ExerciseVideoModal } from "./exercise-video-modal"; import { ExercisePickModal } from "./exercise-pick-modal"; import type { ExerciseWithAttributes } from "../types"; interface ExerciseListItemProps { exercise: ExerciseWithAttributes; muscle: string; onShuffle: (exerciseId: string, muscle: string) => void; onPick: (exerciseId: string) => void; onDelete: (exerciseId: string, muscle: string) => void; isShuffling?: boolean; } const MUSCLE_CONFIGS: Record = { ABDOMINALS: { color: "text-red-600 dark:text-red-400", bg: "bg-red-50 dark:bg-red-950/50" }, BICEPS: { color: "text-purple-600 dark:text-purple-400", bg: "bg-purple-50 dark:bg-purple-950/50" }, BACK: { color: "text-blue-600 dark:text-blue-400", bg: "bg-blue-50 dark:bg-blue-950/50" }, CHEST: { color: "text-green-600 dark:text-green-400", bg: "bg-green-50 dark:bg-green-950/50" }, SHOULDERS: { color: "text-orange-600 dark:text-orange-400", bg: "bg-orange-50 dark:bg-orange-950/50" }, OBLIQUES: { color: "text-pink-600 dark:text-pink-400", bg: "bg-pink-50 dark:bg-pink-950/50" }, }; const DEFAULT_MUSCLE_CONFIG = { color: "text-slate-600 dark:text-slate-400", bg: "bg-slate-50 dark:bg-slate-950/50" }; export function ExerciseListItem({ exercise, muscle, onShuffle, onPick, onDelete, isShuffling }: ExerciseListItemProps) { const t = useI18n(); const locale = useCurrentLocale(); const [showVideo, setShowVideo] = useState(false); const [showPickModal, setShowPickModal] = useState(false); const exerciseName = useMemo(() => (locale === "fr" ? exercise.name : exercise.nameEn), [locale, exercise.name, exercise.nameEn]); const muscleConfig = useMemo(() => MUSCLE_CONFIGS[muscle] || DEFAULT_MUSCLE_CONFIG, [muscle]); const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: exercise.id }); const style = useMemo( () => ({ transform: CSS.Transform.toString(transform), transition, zIndex: isDragging ? 50 : 1, boxShadow: isDragging ? "0 4px 16px 0 rgba(0,0,0,0.10)" : undefined, }), [transform, transition, isDragging], ); // Mémoriser les handlers const handleOpenVideo = useCallback(() => { setShowVideo(true); }, []); const handleClosePickModal = useCallback(() => { setShowPickModal(false); }, []); const handleConfirmPick = useCallback(() => { onPick(exercise.id); }, [onPick, exercise.id]); const handleShuffle = useCallback(() => { onShuffle(exercise.id, muscle); }, [onShuffle, exercise.id, muscle]); const handleDelete = useCallback(() => { onDelete(exercise.id, muscle); }, [onDelete, exercise.id, muscle]); const muscleTitle = useMemo(() => t(("workout_builder.muscles." + muscle.toLowerCase()) as keyof typeof t), [t, muscle]); return (
{/* Drag handle */}
{exercise.fullVideoImageUrl && (
{exerciseName { e.currentTarget.style.display = "none"; }} priority={false} src={exercise.fullVideoImageUrl} width={40} />
)} {/* Badge muscle avec animation */}
{muscle.charAt(0)}
{/* Nom de l'exercice avec indicateurs */}

{exerciseName}

{/* Bouton shuffle */} {/* Bouton delete */}
{/* Video Modal */} {exercise.fullVideoUrl && } {/* Pick Modal */}
); }