|
@@ -1,6 +1,7 @@
|
|
"use client";
|
|
"use client";
|
|
|
|
|
|
-import { ArrowLeft, ArrowRight, CheckCircle, Zap } from "lucide-react";
|
|
|
|
|
|
+import { useState } from "react";
|
|
|
|
+import { ArrowLeft, ArrowRight, CheckCircle, Zap, Plus } from "lucide-react";
|
|
|
|
|
|
import { useI18n } from "locales/client";
|
|
import { useI18n } from "locales/client";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Button } from "@/components/ui/button";
|
|
@@ -9,6 +10,7 @@ import { StepperStepProps } from "../types";
|
|
import { useWorkoutStepper } from "../model/use-workout-stepper";
|
|
import { useWorkoutStepper } from "../model/use-workout-stepper";
|
|
import { StepperHeader } from "./stepper-header";
|
|
import { StepperHeader } from "./stepper-header";
|
|
import { MuscleSelection } from "./muscle-selection";
|
|
import { MuscleSelection } from "./muscle-selection";
|
|
|
|
+import { ExerciseListItem } from "./exercise-list-item";
|
|
import { EquipmentSelection } from "./equipment-selection";
|
|
import { EquipmentSelection } from "./equipment-selection";
|
|
|
|
|
|
function NavigationFooter({
|
|
function NavigationFooter({
|
|
@@ -47,7 +49,7 @@ function NavigationFooter({
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
)}
|
|
- {currentStep !== 1 && (
|
|
|
|
|
|
+ {currentStep === 2 && (
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<CheckCircle className="h-4 w-4 text-blue-500" />
|
|
<CheckCircle className="h-4 w-4 text-blue-500" />
|
|
<span className="font-medium text-slate-700 dark:text-slate-300">
|
|
<span className="font-medium text-slate-700 dark:text-slate-300">
|
|
@@ -107,7 +109,7 @@ function NavigationFooter({
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
)}
|
|
- {currentStep !== 1 && (
|
|
|
|
|
|
+ {currentStep === 2 && (
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<CheckCircle className="h-4 w-4 text-blue-500" />
|
|
<CheckCircle className="h-4 w-4 text-blue-500" />
|
|
<span className="font-medium text-slate-700 dark:text-slate-300">
|
|
<span className="font-medium text-slate-700 dark:text-slate-300">
|
|
@@ -150,10 +152,36 @@ export function WorkoutStepper() {
|
|
toggleMuscle,
|
|
toggleMuscle,
|
|
canProceedToStep2,
|
|
canProceedToStep2,
|
|
canProceedToStep3,
|
|
canProceedToStep3,
|
|
|
|
+ isLoadingExercises,
|
|
|
|
+ exercisesByMuscle,
|
|
|
|
+ exercisesError,
|
|
} = useWorkoutStepper();
|
|
} = useWorkoutStepper();
|
|
|
|
|
|
|
|
+ // État pour les exercices sélectionnés (picked)
|
|
|
|
+ const [pickedExercises, setPickedExercises] = useState<string[]>([]);
|
|
|
|
+
|
|
// Calculer si on peut continuer selon l'étape
|
|
// Calculer si on peut continuer selon l'étape
|
|
- const canContinue = currentStep === 1 ? canProceedToStep2 : currentStep === 2 ? canProceedToStep3 : false;
|
|
|
|
|
|
+ const canContinue = currentStep === 1 ? canProceedToStep2 : currentStep === 2 ? canProceedToStep3 : pickedExercises.length > 0;
|
|
|
|
+
|
|
|
|
+ // Actions pour les exercices
|
|
|
|
+ const handleShuffleExercise = (exerciseId: string, muscle: string) => {
|
|
|
|
+ // TODO: Implémenter la logique pour remplacer l'exercice par un autre
|
|
|
|
+ console.log("Shuffle exercise:", exerciseId, "for muscle:", muscle);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handlePickExercise = (exerciseId: string) => {
|
|
|
|
+ setPickedExercises((prev) => (prev.includes(exerciseId) ? prev.filter((id) => id !== exerciseId) : [...prev, exerciseId]));
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleDeleteExercise = (exerciseId: string, muscle: string) => {
|
|
|
|
+ // TODO: Implémenter la logique pour supprimer l'exercice
|
|
|
|
+ console.log("Delete exercise:", exerciseId, "for muscle:", muscle);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleAddExercise = () => {
|
|
|
|
+ // TODO: Implémenter la logique pour ajouter un exercice
|
|
|
|
+ console.log("Add exercise");
|
|
|
|
+ };
|
|
|
|
|
|
// Calculer l'état des étapes avec traductions
|
|
// Calculer l'état des étapes avec traductions
|
|
const STEPPER_STEPS: StepperStepProps[] = [
|
|
const STEPPER_STEPS: StepperStepProps[] = [
|
|
@@ -197,9 +225,65 @@ export function WorkoutStepper() {
|
|
return <MuscleSelection onToggleMuscle={toggleMuscle} selectedEquipment={selectedEquipment} selectedMuscles={selectedMuscles} />;
|
|
return <MuscleSelection onToggleMuscle={toggleMuscle} selectedEquipment={selectedEquipment} selectedMuscles={selectedMuscles} />;
|
|
case 3:
|
|
case 3:
|
|
return (
|
|
return (
|
|
- <div className="text-center py-20">
|
|
|
|
- <h3 className="text-xl font-semibold mb-4">{t("workout_builder.coming_soon.exercises")}</h3>
|
|
|
|
- <p className="text-slate-600 dark:text-slate-400">{t("workout_builder.coming_soon.exercises_description")}</p>
|
|
|
|
|
|
+ <div className="space-y-6">
|
|
|
|
+ {isLoadingExercises ? (
|
|
|
|
+ <div className="text-center py-20">
|
|
|
|
+ <div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
|
|
|
+ <p className="mt-4 text-slate-600 dark:text-slate-400">{t("workout_builder.loading.exercises")}</p>
|
|
|
|
+ </div>
|
|
|
|
+ ) : exercisesByMuscle.length > 0 ? (
|
|
|
|
+ <div className="max-w-4xl mx-auto">
|
|
|
|
+ {/* Liste des exercices */}
|
|
|
|
+ <div className="bg-white dark:bg-slate-900 rounded-lg border border-slate-200 dark:border-slate-800 overflow-hidden">
|
|
|
|
+ {exercisesByMuscle.map((group, groupIndex) => (
|
|
|
|
+ <div key={group.muscle}>
|
|
|
|
+ {group.exercises.map((exercise, exerciseIndex) => (
|
|
|
|
+ <ExerciseListItem
|
|
|
|
+ exercise={exercise}
|
|
|
|
+ isPicked={pickedExercises.includes(exercise.id)}
|
|
|
|
+ key={exercise.id}
|
|
|
|
+ muscle={group.muscle}
|
|
|
|
+ onDelete={handleDeleteExercise}
|
|
|
|
+ onPick={handlePickExercise}
|
|
|
|
+ onShuffle={handleShuffleExercise}
|
|
|
|
+ />
|
|
|
|
+ ))}
|
|
|
|
+ </div>
|
|
|
|
+ ))}
|
|
|
|
+
|
|
|
|
+ {/* Add exercise button */}
|
|
|
|
+ <div className="border-t border-slate-200 dark:border-slate-800">
|
|
|
|
+ <button
|
|
|
|
+ className="w-full flex items-center gap-3 py-4 px-4 text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-950 transition-colors"
|
|
|
|
+ onClick={handleAddExercise}
|
|
|
|
+ >
|
|
|
|
+ <div className="h-8 w-8 rounded-full bg-blue-600 flex items-center justify-center">
|
|
|
|
+ <Plus className="h-4 w-4 text-white" />
|
|
|
|
+ </div>
|
|
|
|
+ <span className="font-medium">Add</span>
|
|
|
|
+ </button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ {/* Bottom actions */}
|
|
|
|
+ <div className="flex items-center justify-center gap-4 mt-8">
|
|
|
|
+ <Button className="px-8" size="large" variant="outline">
|
|
|
|
+ Save for later
|
|
|
|
+ </Button>
|
|
|
|
+ <Button className="px-8 bg-blue-600 hover:bg-blue-700" size="large">
|
|
|
|
+ Start Workout
|
|
|
|
+ </Button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ ) : exercisesError ? (
|
|
|
|
+ <div className="text-center py-20">
|
|
|
|
+ <p className="text-red-600 dark:text-red-400">{t("workout_builder.error.loading_exercises")}</p>
|
|
|
|
+ </div>
|
|
|
|
+ ) : (
|
|
|
|
+ <div className="text-center py-20">
|
|
|
|
+ <p className="text-slate-600 dark:text-slate-400">{t("workout_builder.no_exercises_found")}</p>
|
|
|
|
+ </div>
|
|
|
|
+ )}
|
|
</div>
|
|
</div>
|
|
);
|
|
);
|
|
default:
|
|
default:
|
|
@@ -208,7 +292,7 @@ export function WorkoutStepper() {
|
|
};
|
|
};
|
|
|
|
|
|
return (
|
|
return (
|
|
- <div className="w-full max-w-4xl mx-auto">
|
|
|
|
|
|
+ <div className="w-full max-w-6xl mx-auto">
|
|
{/* En-tête du stepper */}
|
|
{/* En-tête du stepper */}
|
|
<StepperHeader steps={steps} />
|
|
<StepperHeader steps={steps} />
|
|
|
|
|