Ver código fonte

Merge pull request #21 from Snouzy/style/fix-card

style/fix card
Mat B. 1 mês atrás
pai
commit
138ebf584a

+ 4 - 6
app/[locale]/layout.tsx

@@ -141,7 +141,7 @@ export default async function RootLayout({ params, children }: RootLayoutProps)
 
         <body
           className={cn(
-            "flex flex-col justify-between items-center p-8 min-h-screen max-sm:p-0 max-sm:min-h-full text-sm/[22px] font-normal text-base-content bg-base-200 dark:bg-[#18181b] dark:text-gray-200 antialiased",
+            "flex items-center justify-center min-h-screen w-full p-8 max-sm:p-0 max-sm:min-h-full bg-base-200 dark:bg-[#18181b] dark:text-gray-200 antialiased",
             "bg-hero-light dark:bg-hero-dark",
             GeistMono.variable,
             GeistSans.variable,
@@ -156,11 +156,9 @@ export default async function RootLayout({ params, children }: RootLayoutProps)
             <NextTopLoader color="#FF5722" delay={100} showSpinner={false} />
 
             {/* Main Card Container */}
-            <div className="card bg-white dark:bg-[#232324] shadow-xl w-full max-w-3xl max-sm:rounded-none max-sm:h-full border border-base-200 dark:border-gray-800">
-              <div className="card-body p-0">
-                <Header />
-                <div className="px-2 sm:px-6 pb-6">{children}</div>
-              </div>
+            <div className="card w-full max-w-3xl min-h-[500px] max-h-[90vh] h-[80vh] bg-white dark:bg-[#232324] shadow-xl border border-base-200 dark:border-slate-700 flex flex-col justify-between overflow-hidden max-sm:rounded-none max-sm:h-full rounded-lg">
+              <Header />
+              <div className="flex-1 overflow-auto flex flex-col">{children}</div>
               <Footer />
             </div>
 

+ 1 - 1
app/[locale]/page.tsx

@@ -7,7 +7,7 @@ export default async function HomePage() {
   // const t = await getI18n();
 
   return (
-    <div className="bg-background text-foreground relative flex h-fit flex-col">
+    <div className="bg-background text-foreground relative flex h-fit flex-col h-full">
       <WorkoutStepper />
     </div>
   );

+ 3 - 1
app/[locale]/profile/page.tsx

@@ -27,7 +27,9 @@ export default function ProfilePage() {
   return (
     <div>
       {!sessions.length && <LocalAlert className="my-4" />}
-      <WorkoutSessionHeatmap until={until} values={values} />
+      <div className="px-2 sm:px-6 mt-4">
+        <WorkoutSessionHeatmap until={until} values={values} />
+      </div>
       <WorkoutSessionList />
       <div className="mt-8 flex justify-center">
         <Button onClick={() => router.push("/")} size="large">

+ 1 - 1
locales/fr.ts

@@ -386,7 +386,7 @@ export default {
     consent_banner: "Nous utilisons des cookies pour améliorer votre expérience. En cliquant sur Accepter, vous acceptez nos cookies.",
     about: "À propos",
     profile: "Profil",
-    donate: "Faire un don ❤️",
+    donate: "Faire un don",
     my_account: "Mon compte",
     dashboard: "Tableau de bord",
     home: "Accueil",

+ 10 - 6
src/features/layout/Footer.tsx

@@ -2,6 +2,7 @@ import { Github, Mail, Twitter } from "lucide-react";
 
 import { getI18n } from "locales/server";
 import { TFunction } from "locales/client";
+import { cn } from "@/shared/lib/utils";
 import { Link } from "@/components/ui/link";
 
 const SOCIAL_LINKS = [
@@ -25,15 +26,15 @@ const SOCIAL_LINKS = [
 const NAVIGATION = (t: TFunction) => [
   { name: t("commons.donate"), href: "https://ko-fi.com/workoutcool" },
   { name: t("commons.about"), href: "/about" },
-  { name: t("commons.privacy"), href: "/legal/privacy" },
+  { name: t("commons.privacy"), href: "/legal/privacy", hideOnMobile: true },
 ];
 
 export const Footer = async () => {
   const t = await getI18n();
 
   return (
-    <footer className="border-t border-base-300 dark:border-gray-800 bg-base-100 dark:bg-black px-6 py-4 rounded-b-lg">
-      <div className="flex flex-col sm:flex-row justify-between items-center gap-4">
+    <footer className="border-t border-base-300 dark:border-gray-800 bg-base-100 dark:bg-black px-4 sm:px-6 py-4 rounded-b-lg">
+      <div className="flex  sm:flex-row justify-between items-center gap-4">
         {/* Social Icons */}
         <div className="flex gap-2">
           {SOCIAL_LINKS.map(({ href, icon: Icon, label }) => (
@@ -51,10 +52,13 @@ export const Footer = async () => {
         </div>
 
         {/* Navigation Links */}
-        <div className="flex flex-col sm:flex-row gap-2 sm:gap-4 text-center text-gray-700 dark:text-gray-300">
-          {NAVIGATION(t).map(({ name, href }) => (
+        <div className="flex sm:flex-row gap-2 sm:gap-4 text-center text-gray-700 dark:text-gray-300">
+          {NAVIGATION(t).map(({ name, href, hideOnMobile }) => (
             <Link
-              className="hover:underline hover:text-blue-500 dark:hover:text-blue-400"
+              className={cn(
+                "hover:underline hover:text-blue-500 dark:hover:text-blue-400 text-xs sm:text-sm",
+                hideOnMobile && "hidden sm:block",
+              )}
               href={href}
               key={name}
               size="sm"

+ 4 - 4
src/features/layout/Header.tsx

@@ -25,17 +25,17 @@ export const Header = () => {
   };
 
   return (
-    <div className="navbar bg-base-100 dark:bg-black dark:text-gray-200 px-4 rounded-lg">
+    <div className="navbar bg-base-100 dark:bg-black dark:text-gray-200 px-4 rounded-tl-lg rounded-tr-lg">
       {/* Logo and Title */}
       <div className="navbar-start flex items-center gap-2">
         <Link
-          className="group flex items-center space-x-3 rounded-xl bg-gradient-to-r px-4 py-2 transition-all duration-200 dark:text-gray-200 dark:bg-gray-800"
+          className="group flex items-center space-x-3 rounded-xl bg-gradient-to-r px-2 sm:px-4 py-2 transition-all duration-200 dark:text-gray-200 dark:bg-gray-800"
           href="/"
         >
           <div className="relative flex-none">
             <Image
               alt="workout cool logo"
-              className="h-6 w-6 sm:h-8 sm:w-8 transition-transform duration-200 group-hover:rotate-[20deg] group-hover:scale-110"
+              className="h-10 w-10 sm:h-8 sm:w-8 transition-transform duration-200 group-hover:rotate-[20deg] group-hover:scale-110"
               height={32}
               src={Logo}
               width={32}
@@ -55,7 +55,7 @@ export const Header = () => {
           className="hover:bg-slate-200 dark:hover:bg-gray-800 rounded-full p-2 transition flex"
           href="/"
         >
-          <div className="tooltip" data-tip={t("commons.home")}>
+          <div className="tooltip tooltip-bottom" data-tip={t("commons.home")}>
             <Home className="w-6 h-6 text-blue-500 dark:text-blue-400" />
           </div>
         </Link>

+ 1 - 1
src/features/release-notes/ui/release-notes-dialog.tsx

@@ -18,7 +18,7 @@ export function ReleaseNotesDialog() {
     <Dialog>
       <DialogTrigger asChild>
         <Button aria-label={t("release_notes.release_notes")} className="rounded-full hover:bg-slate-200" size="small" variant="ghost">
-          <div className="tooltip" data-tip={t("commons.changelog")}>
+          <div className="tooltip tooltip-bottom" data-tip={t("commons.changelog")}>
             <Bell className="text-blue-500 dark:text-blue-400 h-6 w-6" />
           </div>
         </Button>

+ 1 - 1
src/features/theme/ThemeToggle.tsx

@@ -19,7 +19,7 @@ export function ThemeToggle() {
       onClick={() => setTheme(resolvedTheme === "light" ? "dark" : "light")}
       variant="ghost"
     >
-      <div className="tooltip" data-tip={resolvedTheme === "light" ? "Dark mode" : "Light mode"}>
+      <div className="tooltip tooltip-bottom" data-tip={resolvedTheme === "light" ? "Dark mode" : "Light mode"}>
         {resolvedTheme === "light" ? (
           <MoonIcon className="text-blue-500 dark:text-blue-400" />
         ) : (

+ 1 - 1
src/features/workout-builder/ui/equipment-selection.tsx

@@ -68,7 +68,7 @@ function EquipmentCard({ equipment, isSelected, onToggle }: EquipmentCardProps)
       )}
       onClick={onToggle}
     >
-      <CardContent className="p-4 h-auto flex flex-col justify-center items-center relative">
+      <CardContent className="p-2 sm:p-4 h-auto flex flex-col justify-center items-center relative">
         <div
           className={cn(
             "absolute top-3 left-3 w-2 h-2 rounded-full transition-colors duration-200",

+ 5 - 7
src/features/workout-builder/ui/exercise-list-item.tsx

@@ -74,7 +74,7 @@ export function ExerciseListItem({ exercise, muscle, onShuffle, onPick, onDelete
     >
       <div className="relative flex items-center justify-between py-2 px-2">
         {/* Section gauche - Infos principales */}
-        <div className="flex items-center gap-4 flex-1 min-w-0">
+        <div className="flex items-center gap-2 sm:gap-4 flex-1 min-w-0">
           {/* Drag handle */}
           <GripVertical className="h-5 w-5 text-slate-400 dark:text-slate-500 cursor-grab active:cursor-grabbing" />
 
@@ -122,18 +122,16 @@ export function ExerciseListItem({ exercise, muscle, onShuffle, onPick, onDelete
         </div>
 
         {/* Section droite - Actions */}
-        <div className="flex items-center gap-2 shrink-0">
+        <div className="flex items-center gap-1 sm:gap-2 shrink-0">
           {/* Bouton shuffle */}
-          <Button onClick={() => onShuffle(exercise.id, muscle)} size="small" variant="outline">
+          <Button className="p-1 sm:p-2" onClick={() => onShuffle(exercise.id, muscle)} size="small" variant="outline">
             <Shuffle className="h-3.5 w-3.5" />
             <span className="hidden sm:inline">{t("workout_builder.exercise.shuffle")}</span>
           </Button>
 
           {/* Bouton pick */}
           <Button
-            className={
-              "bg-blue-50 dark:bg-blue-950/50 hover:bg-blue-100 dark:hover:bg-blue-950 text-blue-600 dark:text-blue-400 border-2 border-blue-200 dark:border-blue-800"
-            }
+            className="p-1 sm:p-2 bg-blue-50 dark:bg-blue-950/50 hover:bg-blue-100 dark:hover:bg-blue-950 text-blue-600 dark:text-blue-400 border-2 border-blue-200 dark:border-blue-800"
             onClick={() => onPick(exercise.id)}
             size="small"
           >
@@ -143,7 +141,7 @@ export function ExerciseListItem({ exercise, muscle, onShuffle, onPick, onDelete
 
           {/* Bouton delete */}
           <Button
-            className="h-9 w-9 bg-red-50 dark:bg-red-950/50 hover:bg-red-100 dark:hover:bg-red-950 text-red-600 dark:text-red-400 border-0 rounded-lg  group-hover:opacity-100 transition-all duration-200 hover:scale-110"
+            className="p-1 sm:p-2 bg-red-50 dark:bg-red-950/50 hover:bg-red-100 dark:hover:bg-red-950 text-red-600 dark:text-red-400 border-0 rounded-lg  group-hover:opacity-100 transition-all duration-200 hover:scale-110"
             onClick={() => onDelete(exercise.id, muscle)}
             size="small"
             variant="ghost"

+ 1 - 13
src/features/workout-builder/ui/exercises-selection.tsx

@@ -4,8 +4,6 @@ import { arrayMove, SortableContext, verticalListSortingStrategy } from "@dnd-ki
 import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
 import { DndContext, closestCenter, PointerSensor, useSensor, useSensors, DragEndEvent } from "@dnd-kit/core";
 
-import { Button } from "@/components/ui/button";
-
 import { useWorkoutStepper } from "../model/use-workout-stepper";
 import { ExerciseListItem } from "./exercise-list-item";
 
@@ -111,22 +109,12 @@ export const ExercisesSelection = ({
                     <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>
+                    <span className="font-medium">{t("commons.add")}</span>
                   </button>
                 </div>
               </div>
             </SortableContext>
           </DndContext>
-          <div className="flex items-center justify-center gap-4 mt-8">
-            <Button
-              className="px-8 bg-blue-600 hover:bg-blue-700"
-              disabled={flatExercises.length === 0}
-              onClick={handleStartWorkout}
-              size="large"
-            >
-              Start Workout
-            </Button>
-          </div>
         </div>
       ) : error ? (
         <div className="text-center py-20">

+ 0 - 1
src/features/workout-builder/ui/muscles.module.css

@@ -6,7 +6,6 @@
 
 .illustration {
   width: 100%;
-  height: auto;
 }
 
 .illustration path {

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

@@ -91,7 +91,7 @@ function StepperStep({ description, isActive, isCompleted, stepNumber, title }:
 export function StepperHeader({ steps }: StepperHeaderProps) {
   const { resolvedTheme } = useTheme();
   return (
-    <div className={cn("w-full", resolvedTheme === "dark" ? "my-8" : "mb-8")}>
+    <div className={cn("w-full my-8 px-2 sm:px-6")}>
       {/* Layout mobile - vertical */}
       <div className="flex flex-col space-y-6 md:hidden">
         {steps.map((step, index) => (

+ 13 - 73
src/features/workout-builder/ui/workout-stepper-footer.tsx

@@ -1,5 +1,5 @@
 "use client";
-import { ArrowLeft, ArrowRight, CheckCircle, Zap } from "lucide-react";
+import { ArrowLeft, ArrowRight } from "lucide-react";
 
 import { useI18n } from "locales/client";
 import { Button } from "@/components/ui/button";
@@ -10,6 +10,7 @@ export function WorkoutBuilderFooter({
   canContinue,
   onPrevious,
   onNext,
+  onStartWorkout,
   selectedEquipment,
   selectedMuscles,
 }: {
@@ -18,6 +19,7 @@ export function WorkoutBuilderFooter({
   canContinue: boolean;
   onPrevious: VoidFunction;
   onNext: VoidFunction;
+  onStartWorkout?: VoidFunction;
   selectedEquipment: any[];
   selectedMuscles: any[];
 }) {
@@ -26,99 +28,37 @@ export function WorkoutBuilderFooter({
   const isFinalStep = currentStep === totalSteps;
 
   return (
-    <div className="w-full">
+    <div className="w-full sticky bottom-0 z-10">
       {/* Mobile layout - vertical stack */}
-      <div className="flex flex-col gap-4 md:hidden">
+      <div className="flex flex-col gap-4 px-2 sm:px-6 pb-2">
         {/* Center stats on top for mobile */}
-        <div className="flex items-center justify-center">
-          <div className="flex items-center gap-4 bg-white dark:bg-slate-800 px-4 py-2 rounded-full dark:border-slate-700 shadow-sm">
-            {currentStep === 1 && (
-              <div className="flex items-center gap-2 text-sm">
-                <Zap className="h-4 w-4 text-emerald-500" />
-                <span className="font-medium text-slate-700 dark:text-slate-300">
-                  {t("workout_builder.stats.equipment_selected", { count: selectedEquipment.length })}
-                </span>
-              </div>
-            )}
-            {currentStep === 2 && (
-              <div className="flex items-center gap-2 text-sm">
-                <CheckCircle className="h-4 w-4 text-blue-500" />
-                <span className="font-medium text-slate-700 dark:text-slate-300">
-                  {t("workout_builder.stats.muscle_selected", { count: selectedMuscles.length })}
-                </span>
-              </div>
-            )}
-          </div>
-        </div>
+        <div className="flex items-center justify-center"></div>
 
         {/* Navigation buttons */}
-        <div className="flex items-center justify-between gap-3">
+        <div className="min-h-12 flex items-center justify-between gap-3 bg-white dark:bg-slate-900 w-full p-0.5 border border-slate-400 dark:border-slate-700 rounded-full">
           {/* Previous button */}
-          <Button className="flex-1" disabled={isFirstStep} onClick={onPrevious} size="default" variant="ghost">
+          <Button className="flex-1 rounded-full min-h-12" disabled={isFirstStep} onClick={onPrevious} size="default" variant="ghost">
             <div className="flex items-center gap-2">
               <ArrowLeft className="h-4 w-4" />
               <span className="font-medium">{t("workout_builder.navigation.previous")}</span>
             </div>
           </Button>
 
-          {/* Next/Complete button */}
+          {/* Next/Start Workout button */}
           <Button
-            className="flex-1"
+            className="flex-1 rounded-full bg-blue-600 hover:bg-blue-700 min-h-12 dark:bg-blue-500 dark:hover:bg-blue-600"
             disabled={!canContinue}
-            onClick={isFinalStep ? () => console.log("Complete workout!") : onNext}
+            onClick={isFinalStep ? () => onStartWorkout?.() : onNext}
             size="default"
             variant="default"
           >
             <div className="flex items-center justify-center gap-2">
-              <span className="font-semibold">
-                {isFinalStep ? t("workout_builder.navigation.complete") : t("workout_builder.navigation.continue")}
-              </span>
-              {!isFinalStep && <ArrowRight className="h-4 w-4" />}
-              {isFinalStep && <CheckCircle className="h-4 w-4" />}
+              <span className="font-semibold">{t("workout_builder.navigation.continue")}</span>
+              <ArrowRight className="h-4 w-4" />
             </div>
           </Button>
         </div>
       </div>
-
-      {/* Desktop layout - horizontal */}
-      <div className="hidden md:flex items-center justify-between">
-        {/* Previous button */}
-        <Button disabled={isFirstStep} onClick={onPrevious} size="large" variant="ghost">
-          <div className="flex items-center gap-2">
-            <ArrowLeft className="h-4 w-4 transition-transform group-hover:-translate-x-1" />
-            <span className="font-medium">{t("workout_builder.navigation.previous")}</span>
-          </div>
-        </Button>
-
-        {/* Center stats */}
-        <div className="flex items-center gap-4 bg-white dark:bg-slate-800 px-6 py-3 rounded-full dark:border-slate-700 shadow-sm">
-          {currentStep === 1 && (
-            <div className="flex items-center gap-2 text-sm">
-              <Zap className="h-4 w-4 text-emerald-500" />
-              <span className="font-medium text-slate-700 dark:text-slate-300">
-                {t("workout_builder.stats.equipment_selected", { count: selectedEquipment.length })}
-              </span>
-            </div>
-          )}
-          {currentStep === 2 && (
-            <div className="flex items-center gap-2 text-sm">
-              <CheckCircle className="h-4 w-4 text-blue-500" />
-              <span className="font-medium text-slate-700 dark:text-slate-300">
-                {t("workout_builder.stats.muscle_selected", { count: selectedMuscles.length })}
-              </span>
-            </div>
-          )}
-          {/* Next step */}
-        </div>
-        {currentStep !== 3 && (
-          <Button disabled={!canContinue} onClick={onNext} size="large" variant="default">
-            <div className="flex items-center gap-2">
-              <span className="font-medium">{t("commons.next")}</span>
-              <ArrowRight className="h-4 w-4 transition-transform group-hover:-translate-x-1" />
-            </div>
-          </Button>
-        )}
-      </div>
     </div>
   );
 }

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

@@ -207,16 +207,17 @@ export function WorkoutStepper() {
   };
 
   return (
-    <div className="w-full max-w-6xl mx-auto">
+    <div className="w-full max-w-6xl mx-auto h-full">
       <StepperHeader steps={steps} />
 
-      <div className="min-h-[400px] mb-8">{renderStepContent()}</div>
+      <div className="px-2 sm:px-6">{renderStepContent()}</div>
 
       <WorkoutBuilderFooter
         canContinue={canContinue}
         currentStep={currentStep}
         onNext={nextStep}
         onPrevious={prevStep}
+        onStartWorkout={handleStartWorkout}
         selectedEquipment={selectedEquipment}
         selectedMuscles={selectedMuscles}
         totalSteps={STEPPER_STEPS.length}

+ 2 - 2
src/features/workout-session/ui/workout-session-header.tsx

@@ -52,8 +52,8 @@ export function WorkoutSessionHeader({
 
   return (
     <>
-      <div className="w-full mb-8">
-        <div className="rounded-xl p-3 bg-slate-50 dark:bg-slate-900/80">
+      <div className="w-full mt-4 mb-8 px-2 sm:px-6">
+        <div className="rounded-xl p-3 bg-slate-50 dark:bg-slate-900/80 border border-slate-200 dark:border-slate-700">
           <div className="flex items-center justify-between mb-4">
             <div className="flex items-center gap-2">
               <div className="w-2 h-2 rounded-full bg-emerald-400 animate-ping"></div>

+ 1 - 1
src/features/workout-session/ui/workout-session-list.tsx

@@ -78,7 +78,7 @@ export function WorkoutSessionList() {
   };
 
   return (
-    <div className="space-y-4">
+    <div className="space-y-4 px-2 sm:px-6">
       <h2 className="text-xl font-bold mt-5 mb-2 text-slate-900 dark:text-slate-200">
         {t("workout_builder.session.history", { count: sessions.length })}
       </h2>

+ 1 - 1
src/features/workout-session/ui/workout-session-sets.tsx

@@ -84,7 +84,7 @@ export function WorkoutSessionSets({
   };
 
   return (
-    <div className="w-full max-w-3xl mx-auto pb-8">
+    <div className="w-full max-w-3xl mx-auto pb-8 px-3 sm:px-6">
       <ol className="relative border-l-2 ml-2 border-slate-200 dark:border-slate-700">
         {session.exercises.map((ex, idx) => {
           const allSetsCompleted = ex.sets.length > 0 && ex.sets.every((set) => set.completed);

+ 1 - 1
src/widgets/language-selector/language-selector.tsx

@@ -24,7 +24,7 @@ export function LanguageSelector() {
 
   return (
     <div className="dropdown dropdown-end">
-      <div className="tooltip" data-tip={t("commons.change_language")}>
+      <div className="tooltip tooltip-bottom" data-tip={t("commons.change_language")}>
         <div
           className="btn btn-ghost btn-circle h-8 w-8 p-0 text-xl flex items-center justify-center hover:bg-slate-200 border-none dark:hover:bg-gray-800 rounded-full"
           role="button"