Browse Source

feat(workout-stepper): enhance step navigation by allowing step clicks to change the current step and improve user interaction
refactor(workout-stepper): update StepperStep and StepperHeader components to support click events for step navigation and improve code readability

Mathias 1 month ago
parent
commit
267d9bcbe3

+ 11 - 12
src/features/workout-builder/model/use-workout-stepper.ts

@@ -25,52 +25,51 @@ export function useWorkoutStepper() {
     isShuffling,
   } = useWorkoutBuilderStore();
 
-  // Validation des étapes
   const canProceedToStep2 = selectedEquipment.length > 0;
   const canProceedToStep3 = selectedMuscles.length > 0;
 
   return {
-    // État
+    // state
     currentStep,
     selectedEquipment,
     selectedMuscles,
 
-    // Exercices
+    // exercises
     exercisesByMuscle,
     isLoadingExercises,
     exercisesError,
 
-    // Navigation
+    // navigation
     goToStep: setStep,
     nextStep,
     prevStep,
 
-    // Équipements
+    // equipment
     toggleEquipment,
     clearEquipment,
 
-    // Muscles
+    // muscles
     toggleMuscle,
     clearMuscles,
 
-    // Validation
+    // validation
     canProceedToStep2,
     canProceedToStep3,
 
-    // Fetch
+    // fetch
     fetchExercises,
 
-    // Order
+    // order
     exercisesOrder,
     setExercisesOrder,
 
-    // Shuffle
+    // shuffle
     shuffleExercise,
 
-    // Additional
+    // additional
     isShuffling,
 
-    // Pick
+    // pick
     pickExercise,
   };
 }

+ 44 - 14
src/features/workout-builder/ui/stepper-header.tsx

@@ -10,20 +10,44 @@ import { StepperStepProps } from "../types";
 
 interface StepperHeaderProps {
   steps: StepperStepProps[];
+  currentStep: number;
+  onStepClick?: (stepNumber: number) => void;
 }
 
-function StepperStep({ description, isActive, isCompleted, stepNumber, title }: StepperStepProps) {
+function StepperStep({
+  description,
+  isActive,
+  isCompleted,
+  stepNumber,
+  title,
+  currentStep,
+  onStepClick,
+}: StepperStepProps & { currentStep: number; onStepClick?: (stepNumber: number) => void }) {
+  const canClick = stepNumber < currentStep || isCompleted;
+
+  const handleClick = () => {
+    if (canClick && onStepClick) {
+      onStepClick(stepNumber);
+    }
+  };
+
   return (
     <>
       {/* Layout mobile - vertical avec texte à droite */}
       <div className="flex items-center text-left md:hidden">
         {/* Cercle */}
         <div
-          className={cn("flex h-12 w-12 items-center justify-center rounded-full border-2 transition-all duration-200 flex-shrink-0", {
-            "border-green-500 bg-green-500 text-white": isCompleted,
-            "border-blue-500 bg-blue-500 text-white": isActive,
-            "border-gray-300 bg-gray-100 text-gray-400 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-500": !isActive && !isCompleted,
-          })}
+          className={cn(
+            "flex h-12 w-12 items-center justify-center rounded-full border-2 transition-all duration-200 flex-shrink-0",
+            {
+              "border-green-500 bg-green-500 text-white": isCompleted,
+              "border-blue-500 bg-blue-500 text-white": isActive,
+              "border-gray-300 bg-gray-100 text-gray-400 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-500":
+                !isActive && !isCompleted,
+            },
+            canClick ? "cursor-pointer hover:scale-105" : "cursor-default",
+          )}
+          onClick={handleClick}
         >
           {isCompleted ? <Check className="h-6 w-6" /> : <span className="text-sm font-semibold">{stepNumber}</span>}
         </div>
@@ -54,11 +78,17 @@ function StepperStep({ description, isActive, isCompleted, stepNumber, title }:
       <div className="hidden md:flex flex-col items-center text-center">
         {/* Cercle */}
         <div
-          className={cn("flex h-12 w-12 items-center justify-center rounded-full border-2 transition-all duration-200", {
-            "border-green-500 bg-green-500 text-white": isCompleted,
-            "border-blue-500 bg-blue-500 text-white": isActive,
-            "border-gray-300 bg-gray-100 text-gray-400 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-500": !isActive && !isCompleted,
-          })}
+          className={cn(
+            "flex h-12 w-12 items-center justify-center rounded-full border-2 transition-all duration-200",
+            {
+              "border-green-500 bg-green-500 text-white": isCompleted,
+              "border-blue-500 bg-blue-500 text-white": isActive,
+              "border-gray-300 bg-gray-100 text-gray-400 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-500":
+                !isActive && !isCompleted,
+            },
+            canClick ? "cursor-pointer hover:scale-105" : "cursor-default",
+          )}
+          onClick={handleClick}
         >
           {isCompleted ? <Check className="h-6 w-6" /> : <span className="text-sm font-semibold">{stepNumber}</span>}
         </div>
@@ -88,7 +118,7 @@ function StepperStep({ description, isActive, isCompleted, stepNumber, title }:
   );
 }
 
-export function StepperHeader({ steps }: StepperHeaderProps) {
+export function StepperHeader({ steps, currentStep, onStepClick }: StepperHeaderProps) {
   const { resolvedTheme } = useTheme();
   return (
     <div className={cn("w-full my-8 px-2 sm:px-6")}>
@@ -96,7 +126,7 @@ export function StepperHeader({ steps }: StepperHeaderProps) {
       <div className="flex flex-col space-y-6 md:hidden">
         {steps.map((step, index) => (
           <div className="relative" key={step.stepNumber}>
-            <StepperStep {...step} />
+            <StepperStep {...step} currentStep={currentStep} onStepClick={onStepClick} />
 
             {/* Ligne de connexion verticale */}
             {index < steps.length - 1 && (
@@ -119,7 +149,7 @@ export function StepperHeader({ steps }: StepperHeaderProps) {
           <React.Fragment key={step.stepNumber}>
             {/* Étape */}
             <div className="flex flex-col items-center">
-              <StepperStep {...step} />
+              <StepperStep {...step} currentStep={currentStep} onStepClick={onStepClick} />
             </div>
 
             {/* Ligne de connexion horizontale */}

+ 9 - 2
src/features/workout-builder/ui/workout-stepper.tsx

@@ -21,7 +21,7 @@ import { MuscleSelection } from "./muscle-selection";
 import { ExercisesSelection } from "./exercises-selection";
 import { EquipmentSelection } from "./equipment-selection";
 
-import type { ExerciseWithAttributes } from "../types";
+import type { ExerciseWithAttributes, WorkoutBuilderStep } from "../types";
 
 export function WorkoutStepper() {
   const { loadSessionFromLocal } = useWorkoutSession();
@@ -48,6 +48,7 @@ export function WorkoutStepper() {
     shuffleExercise,
     pickExercise,
     isShuffling,
+    goToStep,
   } = useWorkoutStepper();
 
   useEffect(() => {
@@ -146,6 +147,12 @@ export function WorkoutStepper() {
     if (fromSession) setFromSession(null);
   };
 
+  const handleStepClick = (stepNumber: number) => {
+    if (stepNumber < currentStep) {
+      goToStep(stepNumber as WorkoutBuilderStep);
+    }
+  };
+
   if (showCongrats && !isWorkoutActive) {
     return (
       <div className="flex flex-col items-center justify-center py-16 h-full">
@@ -238,7 +245,7 @@ export function WorkoutStepper() {
 
   return (
     <div className="w-full max-w-6xl mx-auto h-full">
-      <StepperHeader steps={steps} />
+      <StepperHeader currentStep={currentStep} onStepClick={handleStepClick} steps={steps} />
 
       <div className="px-2 sm:px-6">{renderStepContent()}</div>