Files
parths-life-manager/src/components/modules/FitnessView.tsx
T

1189 lines
57 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import React, { useState } from 'react';
import {
Dumbbell,
Flame,
Zap,
Activity,
Droplets,
Plus,
Trash2,
Scale,
Info,
CheckCircle2,
X,
Sparkles,
Calculator,
ShieldCheck,
TrendingUp,
Target,
Utensils,
Check
} from 'lucide-react';
import { WorkoutSession, DailyNutrition, MuscleRecovery, WorkoutExercise } from '@/lib/types';
import { SCIENCE_TIPS, getFormattedDate } from '@/lib/initialData';
import { playNotificationChime } from '@/lib/audio';
interface FitnessViewProps {
workouts: WorkoutSession[];
nutrition: DailyNutrition;
muscleRecovery: MuscleRecovery[];
onAddWorkout: (workout: Omit<WorkoutSession, 'id'>) => void;
onDeleteWorkout: (id: string) => void;
onUpdateNutrition: (updates: Partial<DailyNutrition>) => void;
}
export const FitnessView: React.FC<FitnessViewProps> = ({
workouts,
nutrition,
muscleRecovery,
onAddWorkout,
onDeleteWorkout,
onUpdateNutrition,
}) => {
// Modal states
const [isWorkoutModalOpen, setIsWorkoutModalOpen] = useState(false);
const [isNutritionModalOpen, setIsNutritionModalOpen] = useState(false);
// 1RM Calculator state
const [calcWeight, setCalcWeight] = useState<number>(85);
const [calcReps, setCalcReps] = useState<number>(6);
// Calculate 1RM via Epley Formula: 1RM = Weight * (1 + Reps / 30)
const estimated1RM = Math.max(1, Math.round(calcWeight * (1 + calcReps / 30)));
// Daily Macro & TDEE Calculator state
const [macroWeight, setMacroWeight] = useState<number>(nutrition.bodyweightKg || 78.5);
const [macroGoal, setMacroGoal] = useState<'cut' | 'maintain' | 'bulk'>('maintain');
const [macroActivity, setMacroActivity] = useState<'sedentary' | 'moderate' | 'intense'>('moderate');
const [isSynced, setIsSynced] = useState(false);
// Scientific Macro Calculations:
// Activity Multipliers on bodyweight: Sedentary: 30 kcal/kg, Moderate: 33 kcal/kg, Intense: 36 kcal/kg
const activityMultiplier = macroActivity === 'sedentary' ? 30 : macroActivity === 'intense' ? 36 : 33;
const maintenanceTDEE = Math.round(macroWeight * activityMultiplier);
let calcTargetCalories = maintenanceTDEE;
let calcTargetProtein = Math.round(macroWeight * 2.0); // g (optimal 2.0g/kg)
let calcTargetFats = Math.round(macroWeight * 0.9); // g
if (macroGoal === 'cut') {
calcTargetCalories = Math.round(maintenanceTDEE * 0.80); // 20% deficit
calcTargetProtein = Math.round(macroWeight * 2.3); // High protein to preserve lean body mass in a cut
calcTargetFats = Math.round(macroWeight * 0.75);
} else if (macroGoal === 'bulk') {
calcTargetCalories = Math.round(maintenanceTDEE * 1.10); // 10% clean surplus for hypertrophy
calcTargetProtein = Math.round(macroWeight * 2.0);
calcTargetFats = Math.round(macroWeight * 1.0);
}
// Carbs = Remaining calories / 4 kcal per gram (primary glycogen driver)
const caloriesFromProteinAndFat = (calcTargetProtein * 4) + (calcTargetFats * 9);
const calcTargetCarbs = Math.max(40, Math.round((calcTargetCalories - caloriesFromProteinAndFat) / 4));
const handleApplyMacroTargets = () => {
onUpdateNutrition({
calorieTarget: calcTargetCalories,
proteinTarget: calcTargetProtein,
carbsTarget: calcTargetCarbs,
fatsTarget: calcTargetFats,
bodyweightKg: macroWeight,
});
setIsSynced(true);
playNotificationChime('complete');
setTimeout(() => setIsSynced(false), 2500);
};
// Log Workout Form State
const [workoutName, setWorkoutName] = useState('Push Hypertrophy');
const [workoutDate, setWorkoutDate] = useState(getFormattedDate(0));
const [workoutMuscles, setWorkoutMuscles] = useState('Chest, Shoulders, Triceps');
const [workoutNotes, setWorkoutNotes] = useState('');
const [exerciseList, setExerciseList] = useState<{
name: string;
muscleGroup: string;
weight: number;
reps: number;
rpe: number;
setsCount: number;
}[]>([
{ name: 'Incline Dumbbell Press', muscleGroup: 'Chest', weight: 32, reps: 8, rpe: 8.5, setsCount: 3 },
]);
const addExerciseRow = () => {
setExerciseList([
...exerciseList,
{ name: '', muscleGroup: 'Chest', weight: 60, reps: 8, rpe: 8, setsCount: 3 },
]);
};
const removeExerciseRow = (index: number) => {
setExerciseList(exerciseList.filter((_, i) => i !== index));
};
const handleCreateWorkout = (e: React.FormEvent) => {
e.preventDefault();
if (!workoutName.trim() || exerciseList.length === 0) return;
let totalVolume = 0;
const constructedExercises: WorkoutExercise[] = exerciseList.map((ex) => {
const sets = [];
for (let s = 1; s <= ex.setsCount; s++) {
sets.push({
setNumber: s,
weight: ex.weight,
reps: ex.reps,
rpe: ex.rpe,
});
totalVolume += ex.weight * ex.reps;
}
return {
name: ex.name || 'Exercise',
muscleGroup: ex.muscleGroup,
sets,
};
});
onAddWorkout({
name: workoutName.trim(),
date: workoutDate,
muscleGroups: workoutMuscles.split(',').map((m) => m.trim()),
volumeLoad: totalVolume,
exercises: constructedExercises,
notes: workoutNotes.trim() || undefined,
});
setIsWorkoutModalOpen(false);
playNotificationChime('streak');
};
// Water Increment / Decrement
const handleWaterChange = (delta: number) => {
const updated = Math.max(0, parseFloat((nutrition.waterLiters + delta).toFixed(2)));
onUpdateNutrition({ waterLiters: updated });
playNotificationChime('click');
};
// Creatine Toggle
const handleCreatineToggle = () => {
onUpdateNutrition({ creatineTaken: !nutrition.creatineTaken });
playNotificationChime(nutrition.creatineTaken ? 'click' : 'complete');
};
// Nutrition Form State
const [editCalories, setEditCalories] = useState(nutrition.calories);
const [editProtein, setEditProtein] = useState(nutrition.protein);
const [editCarbs, setEditCarbs] = useState(nutrition.carbs);
const [editFats, setEditFats] = useState(nutrition.fats);
const [editWeight, setEditWeight] = useState(nutrition.bodyweightKg);
const handleSaveNutrition = (e: React.FormEvent) => {
e.preventDefault();
onUpdateNutrition({
calories: Number(editCalories),
protein: Number(editProtein),
carbs: Number(editCarbs),
fats: Number(editFats),
bodyweightKg: Number(editWeight),
});
setIsNutritionModalOpen(false);
playNotificationChime('click');
};
const proteinRatio = (nutrition.protein / nutrition.bodyweightKg).toFixed(2);
const isOptimalProtein = parseFloat(proteinRatio) >= 1.6;
return (
<div className="space-y-8 pb-12">
{/* Header Banner */}
<div className="relative overflow-hidden rounded-3xl bg-gradient-to-r from-[#0d1f18] via-[#0d1627] to-[#121c33] border border-lime-500/30 p-6 md:p-8 shadow-2xl">
<div className="absolute -right-12 -top-12 w-64 h-64 bg-lime-500/10 rounded-full blur-3xl pointer-events-none" />
<div className="absolute -left-12 -bottom-12 w-64 h-64 bg-cyan-500/10 rounded-full blur-3xl pointer-events-none" />
<div className="relative z-10 flex flex-col md:flex-row md:items-center justify-between gap-6">
<div className="space-y-2">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-lime-500/10 border border-lime-500/30 text-lime-400 text-xs font-semibold tracking-wide">
<Zap className="w-3.5 h-3.5" />
<span>Evidence-Based Hypertrophy & Metabolism Lab</span>
</div>
<h2 className="text-2xl md:text-3xl font-extrabold text-white tracking-tight">
Science of <span className="bg-clip-text text-transparent bg-gradient-to-r from-lime-400 to-emerald-400">Progressive Overload</span>
</h2>
<p className="text-sm text-slate-400 max-w-2xl">
Tension over guesswork. Track volume load (kg × reps), monitor your Muscle Protein Synthesis (MPS) recovery windows, and hit precise macronutrient thresholds for optimal adaptation.
</p>
</div>
<div className="flex flex-wrap gap-3">
<button
onClick={() => setIsWorkoutModalOpen(true)}
className="flex items-center gap-2 px-5 py-3 rounded-2xl bg-gradient-to-r from-lime-500 to-emerald-500 text-slate-950 font-bold text-sm shadow-lg shadow-lime-500/20 hover:scale-105 active:scale-95 transition-all cursor-pointer"
>
<Dumbbell className="w-4 h-4" />
<span>Log Scientific Workout</span>
</button>
<button
onClick={() => setIsNutritionModalOpen(true)}
className="flex items-center gap-2 px-5 py-3 rounded-2xl bg-slate-900 border border-slate-800 hover:bg-slate-800 text-slate-200 font-semibold text-sm transition-all hover:scale-105 active:scale-95 cursor-pointer"
>
<Scale className="w-4 h-4 text-lime-400" />
<span>Adjust Macros</span>
</button>
</div>
</div>
</div>
{/* SECTION 1: MACRONUTRIENT & METABOLIC FUEL STATION */}
<div className="space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Flame className="w-5 h-5 text-lime-400" />
<h3 className="text-lg font-bold text-slate-100">Daily Macronutrient Fuel Station</h3>
</div>
<span className="text-xs text-slate-400">Bodyweight: <strong className="text-white">{nutrition.bodyweightKg} kg</strong></span>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{/* Calories Card */}
<div className="glass-card p-5 border-l-4 border-l-lime-500">
<div className="flex items-center justify-between">
<span className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Total Energy (Calories)</span>
<span className="text-xs font-bold text-lime-400">
{Math.round((nutrition.calories / nutrition.calorieTarget) * 100)}%
</span>
</div>
<div className="mt-3 flex items-baseline gap-2">
<span className="text-3xl font-extrabold text-white">{nutrition.calories}</span>
<span className="text-xs text-slate-400">/ {nutrition.calorieTarget} kcal</span>
</div>
<div className="w-full h-2 bg-slate-900 rounded-full mt-3 overflow-hidden border border-slate-800">
<div
className="h-full bg-lime-400 rounded-full"
style={{ width: `${Math.min(100, (nutrition.calories / nutrition.calorieTarget) * 100)}%` }}
/>
</div>
</div>
{/* Protein Card (Scientific g/kg metric) */}
<div className="glass-card p-5 border-l-4 border-l-cyan-500">
<div className="flex items-center justify-between">
<span className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Protein (MPS)</span>
<span className={`text-[10px] font-bold px-2 py-0.5 rounded-full border ${
isOptimalProtein ? 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30' : 'bg-amber-500/10 text-amber-400 border-amber-500/30'
}`}>
{proteinRatio} g/kg
</span>
</div>
<div className="mt-3 flex items-baseline gap-2">
<span className="text-3xl font-extrabold text-white">{nutrition.protein}g</span>
<span className="text-xs text-slate-400">/ {nutrition.proteinTarget}g target</span>
</div>
<p className="text-[11px] text-slate-400 mt-2 flex items-center gap-1">
<ShieldCheck className="w-3.5 h-3.5 text-cyan-400" />
<span>Optimal range: 1.6 - 2.2 g/kg</span>
</p>
</div>
{/* Carbohydrates & Fats */}
<div className="glass-card p-5 border-l-4 border-l-amber-500">
<div className="flex items-center justify-between">
<span className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Glycogen & Hormones</span>
<span className="text-xs text-slate-400">Carbs & Fats</span>
</div>
<div className="mt-3 flex items-center justify-between">
<div>
<span className="text-xl font-bold text-amber-400">{nutrition.carbs}g</span>
<p className="text-[10px] text-slate-400">Carbs (Glycogen)</p>
</div>
<div className="text-right">
<span className="text-xl font-bold text-orange-400">{nutrition.fats}g</span>
<p className="text-[10px] text-slate-400">Fats (Lipids)</p>
</div>
</div>
<div className="w-full h-2 bg-slate-900 rounded-full mt-3 overflow-hidden border border-slate-800 flex">
<div
className="bg-amber-400 h-full"
style={{ width: `${Math.min(65, (nutrition.carbs / nutrition.carbsTarget) * 65)}%` }}
/>
<div
className="bg-orange-400 h-full ml-1"
style={{ width: `${Math.min(35, (nutrition.fats / nutrition.fatsTarget) * 35)}%` }}
/>
</div>
</div>
{/* Hydration & Creatine Station */}
<div className="glass-card p-5 border-l-4 border-l-blue-500 flex flex-col justify-between">
<div className="flex items-center justify-between">
<span className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Hydration & Ergogenics</span>
<Droplets className="w-4 h-4 text-blue-400" />
</div>
<div className="my-2 flex items-center justify-between">
<div>
<span className="text-2xl font-extrabold text-white">{nutrition.waterLiters}L</span>
<p className="text-[10px] text-slate-400">Water Intake</p>
</div>
<div className="flex items-center gap-1">
<button
onClick={() => handleWaterChange(-0.25)}
className="w-7 h-7 rounded-lg bg-slate-900 border border-slate-800 text-xs font-bold text-slate-300 hover:text-white"
title="Remove 250ml"
>
-
</button>
<button
onClick={() => handleWaterChange(0.25)}
className="w-7 h-7 rounded-lg bg-blue-500/20 border border-blue-500/40 text-xs font-bold text-blue-300 hover:bg-blue-500/30"
title="Add 250ml"
>
+
</button>
</div>
</div>
{/* Creatine Checkbox */}
<button
onClick={handleCreatineToggle}
className={`w-full py-1.5 px-3 rounded-xl border flex items-center justify-between text-xs font-semibold transition-all cursor-pointer ${
nutrition.creatineTaken
? 'bg-lime-500/10 border-lime-500/40 text-lime-400'
: 'bg-slate-900 border-slate-800 text-slate-400 hover:border-slate-700'
}`}
>
<span>Creatine Monohydrate (5g)</span>
<CheckCircle2 className={`w-4 h-4 ${nutrition.creatineTaken ? 'fill-lime-400/20 stroke-lime-400' : 'opacity-40'}`} />
</button>
</div>
</div>
</div>
{/* SECTION 2: MUSCLE RECOVERY & PROTEIN SYNTHESIS MATRIX */}
<div className="glass-card p-6 space-y-4">
<div className="flex items-center justify-between border-b border-slate-800 pb-3">
<div>
<h3 className="text-base font-bold text-slate-100 flex items-center gap-2">
<Activity className="w-5 h-5 text-lime-400" />
<span>Muscle Protein Synthesis (MPS) & Recovery Matrix</span>
</h3>
<p className="text-xs text-slate-400 mt-0.5">
Based on the 48–72h physiological recovery window required for tissue remodeling.
</p>
</div>
<span className="text-xs px-2.5 py-1 rounded-full bg-slate-900 border border-slate-800 text-slate-400 font-semibold hidden sm:inline">
Hypertrophy Window
</span>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{muscleRecovery.map((rec) => {
const isReady = rec.status === 'ready';
const isRecovering = rec.status === 'recovering';
return (
<div
key={rec.muscle}
className="p-4 rounded-xl bg-slate-900/80 border border-slate-800 flex flex-col justify-between space-y-3"
>
<div className="flex items-center justify-between">
<span className="text-sm font-bold text-slate-200">{rec.muscle}</span>
<span className={`text-[10px] font-extrabold uppercase px-2 py-0.5 rounded-full border ${
isReady
? 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30'
: isRecovering
? 'bg-amber-500/10 text-amber-400 border-amber-500/30'
: 'bg-rose-500/10 text-rose-400 border-rose-500/30'
}`}>
{rec.status}
</span>
</div>
<div>
<div className="flex items-center justify-between text-xs text-slate-400 mb-1">
<span>Readiness: {rec.percentRecovered}%</span>
<span>Trained {rec.lastTrainedDaysAgo === 0 ? 'Today' : `${rec.lastTrainedDaysAgo}d ago`}</span>
</div>
<div className="w-full h-2 bg-slate-950 rounded-full overflow-hidden border border-slate-800">
<div
className={`h-full rounded-full transition-all ${
isReady ? 'bg-emerald-400' : isRecovering ? 'bg-amber-400' : 'bg-rose-400'
}`}
style={{ width: `${rec.percentRecovered}%` }}
/>
</div>
</div>
</div>
);
})}
</div>
</div>
{/* SECTION 3: INTERACTIVE SCIENTIFIC FITNESS TOOLS & CALCULATORS */}
<div className="space-y-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Calculator className="w-5 h-5 text-cyan-400" />
<h3 className="text-lg font-bold text-slate-100">Interactive Scientific Fitness Tools</h3>
</div>
<span className="text-xs text-slate-400">Precision Biometrics & Training Zones</span>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* TOOL 1: 1RM & Percentage Training Zones Calculator */}
<div className="glass-card p-6 flex flex-col justify-between space-y-5 border-t-4 border-t-cyan-500">
<div>
<div className="flex items-center justify-between mb-1">
<div className="flex items-center gap-2">
<Dumbbell className="w-5 h-5 text-cyan-400" />
<h4 className="font-bold text-slate-100 text-base">One-Rep Max (1RM) Calculator</h4>
</div>
<span className="text-[10px] font-bold px-2 py-0.5 rounded-full bg-cyan-500/10 text-cyan-300 border border-cyan-500/30">
Epley Formula
</span>
</div>
<p className="text-xs text-slate-400 leading-relaxed mb-4">
Calculate estimated max strength without injury risk and identify your exact percentage training zones.
</p>
{/* Weight Input with Steppers & Quick Chips */}
<div className="space-y-3 p-4 rounded-xl bg-slate-900/80 border border-slate-800">
<div className="flex items-center justify-between">
<label className="text-xs font-semibold text-slate-300">Weight Lifted</label>
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => setCalcWeight(Math.max(5, calcWeight - 2.5))}
className="w-6 h-6 rounded bg-slate-800 text-slate-300 hover:text-white text-xs font-bold cursor-pointer"
>
-
</button>
<span className="text-sm font-bold text-cyan-400 font-mono w-16 text-center">{calcWeight} kg</span>
<button
type="button"
onClick={() => setCalcWeight(calcWeight + 2.5)}
className="w-6 h-6 rounded bg-slate-800 text-slate-300 hover:text-white text-xs font-bold cursor-pointer"
>
+
</button>
</div>
</div>
<input
type="range"
min="20"
max="250"
step="2.5"
value={calcWeight}
onChange={(e) => setCalcWeight(Number(e.target.value))}
className="w-full accent-cyan-400 cursor-pointer"
/>
{/* Quick Presets */}
<div className="flex items-center gap-1.5 pt-1">
<span className="text-[10px] text-slate-500 mr-1">Presets:</span>
{[60, 80, 100, 120, 140].map((w) => (
<button
key={w}
type="button"
onClick={() => setCalcWeight(w)}
className={`text-[10px] px-2 py-0.5 rounded-md border font-semibold transition-colors cursor-pointer ${
calcWeight === w
? 'bg-cyan-500/20 text-cyan-300 border-cyan-500/50'
: 'bg-slate-950 text-slate-400 border-slate-800 hover:text-slate-200'
}`}
>
{w}kg
</button>
))}
</div>
</div>
{/* Reps Input with Segmented Pills */}
<div className="space-y-2 mt-4 p-4 rounded-xl bg-slate-900/80 border border-slate-800">
<div className="flex items-center justify-between">
<label className="text-xs font-semibold text-slate-300">Reps Completed</label>
<span className="text-sm font-bold text-cyan-400 font-mono">{calcReps} reps</span>
</div>
<div className="grid grid-cols-5 sm:grid-cols-9 gap-1.5 pt-1">
{[1, 2, 3, 4, 5, 6, 8, 10, 12].map((r) => (
<button
key={r}
type="button"
onClick={() => setCalcReps(r)}
className={`py-1.5 rounded-lg text-xs font-bold transition-all cursor-pointer ${
calcReps === r
? 'bg-cyan-500 text-slate-950 shadow-md shadow-cyan-500/30'
: 'bg-slate-950 text-slate-400 border border-slate-800 hover:text-slate-200'
}`}
>
{r}
</button>
))}
</div>
</div>
{/* Estimated 1RM Readout */}
<div className="mt-4 p-4 rounded-2xl bg-cyan-950/20 border border-cyan-500/40 text-center relative overflow-hidden">
<div className="absolute top-0 right-0 p-2 text-cyan-400 opacity-20 pointer-events-none">
<Sparkles className="w-12 h-12" />
</div>
<span className="text-xs font-bold text-cyan-300 uppercase tracking-wider">Estimated 1-Rep Max</span>
<div className="text-4xl font-black text-white mt-1 tracking-tight">
{estimated1RM} <span className="text-lg font-bold text-cyan-400">kg</span>
</div>
<p className="text-[11px] text-slate-400 mt-1 font-mono">
1RM = {calcWeight}kg × (1 + {calcReps}/30)
</p>
</div>
{/* Explicit Percentage Training Zones: 90%, 80%, 70% */}
<div className="mt-4 space-y-2">
<div className="text-xs font-bold text-slate-400 uppercase tracking-wider">Percentage Training Zones</div>
<div className="grid grid-cols-3 gap-2">
{/* 90% Zone */}
<div className="p-3 rounded-xl bg-slate-900 border border-rose-500/30 text-center space-y-1">
<span className="text-[11px] font-extrabold text-rose-400 uppercase tracking-wider block">90% 1RM</span>
<div className="text-xl font-extrabold text-white">{Math.round(estimated1RM * 0.90)}kg</div>
<span className="text-[10px] text-slate-300 font-semibold block">3 - 4 Reps</span>
<span className="text-[9px] text-slate-500 block">Max Strength & Power</span>
</div>
{/* 80% Zone */}
<div className="p-3 rounded-xl bg-slate-900 border border-amber-500/30 text-center space-y-1">
<span className="text-[11px] font-extrabold text-amber-400 uppercase tracking-wider block">80% 1RM</span>
<div className="text-xl font-extrabold text-white">{Math.round(estimated1RM * 0.80)}kg</div>
<span className="text-[10px] text-slate-300 font-semibold block">6 - 8 Reps</span>
<span className="text-[9px] text-slate-500 block">Heavy Hypertrophy</span>
</div>
{/* 70% Zone */}
<div className="p-3 rounded-xl bg-slate-900 border border-cyan-500/30 text-center space-y-1">
<span className="text-[11px] font-extrabold text-cyan-400 uppercase tracking-wider block">70% 1RM</span>
<div className="text-xl font-extrabold text-white">{Math.round(estimated1RM * 0.70)}kg</div>
<span className="text-[10px] text-slate-300 font-semibold block">10 - 12 Reps</span>
<span className="text-[9px] text-slate-500 block">Volume & Endurance</span>
</div>
</div>
</div>
</div>
</div>
{/* TOOL 2: Daily Macro Calculator */}
<div className="glass-card p-6 flex flex-col justify-between space-y-5 border-t-4 border-t-lime-500">
<div>
<div className="flex items-center justify-between mb-1">
<div className="flex items-center gap-2">
<Utensils className="w-5 h-5 text-lime-400" />
<h4 className="font-bold text-slate-100 text-base">Scientific Daily Macro Calculator</h4>
</div>
<span className="text-[10px] font-bold px-2 py-0.5 rounded-full bg-lime-500/10 text-lime-300 border border-lime-500/30">
TDEE + MPS
</span>
</div>
<p className="text-xs text-slate-400 leading-relaxed mb-4">
Calculate custom macronutrient requirements based on your current bodyweight and physique goal.
</p>
{/* Bodyweight Input */}
<div className="space-y-3 p-4 rounded-xl bg-slate-900/80 border border-slate-800">
<div className="flex items-center justify-between">
<label className="text-xs font-semibold text-slate-300">Current Bodyweight</label>
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => setMacroWeight(Math.max(40, parseFloat((macroWeight - 0.5).toFixed(1))))}
className="w-6 h-6 rounded bg-slate-800 text-slate-300 hover:text-white text-xs font-bold cursor-pointer"
>
-
</button>
<span className="text-sm font-bold text-lime-400 font-mono w-20 text-center">{macroWeight} kg</span>
<button
type="button"
onClick={() => setMacroWeight(parseFloat((macroWeight + 0.5).toFixed(1)))}
className="w-6 h-6 rounded bg-slate-800 text-slate-300 hover:text-white text-xs font-bold cursor-pointer"
>
+
</button>
</div>
</div>
<input
type="range"
min="50"
max="130"
step="0.5"
value={macroWeight}
onChange={(e) => setMacroWeight(Number(e.target.value))}
className="w-full accent-lime-400 cursor-pointer"
/>
</div>
{/* Goal Selector: Cut, Maintain, Lean Bulk */}
<div className="space-y-2 mt-4">
<label className="text-xs font-semibold text-slate-300">Physique Goal</label>
<div className="grid grid-cols-3 gap-2">
<button
type="button"
onClick={() => setMacroGoal('cut')}
className={`p-3 rounded-xl border text-left transition-all cursor-pointer ${
macroGoal === 'cut'
? 'bg-rose-500/10 border-rose-500 text-rose-300 shadow-md shadow-rose-500/20'
: 'bg-slate-900 border-slate-800 text-slate-400 hover:text-slate-200'
}`}
>
<span className="text-xs font-bold block">🔥 Cut</span>
<span className="text-[10px] text-slate-400 block mt-0.5">-20% Deficit</span>
<span className="text-[9px] text-rose-400 block font-semibold">2.3g/kg Protein</span>
</button>
<button
type="button"
onClick={() => setMacroGoal('maintain')}
className={`p-3 rounded-xl border text-left transition-all cursor-pointer ${
macroGoal === 'maintain'
? 'bg-cyan-500/10 border-cyan-500 text-cyan-300 shadow-md shadow-cyan-500/20'
: 'bg-slate-900 border-slate-800 text-slate-400 hover:text-slate-200'
}`}
>
<span className="text-xs font-bold block">⚖️ Maintain</span>
<span className="text-[10px] text-slate-400 block mt-0.5">TDEE Balance</span>
<span className="text-[9px] text-cyan-400 block font-semibold">2.0g/kg Protein</span>
</button>
<button
type="button"
onClick={() => setMacroGoal('bulk')}
className={`p-3 rounded-xl border text-left transition-all cursor-pointer ${
macroGoal === 'bulk'
? 'bg-lime-500/10 border-lime-500 text-lime-300 shadow-md shadow-lime-500/20'
: 'bg-slate-900 border-slate-800 text-slate-400 hover:text-slate-200'
}`}
>
<span className="text-xs font-bold block">🚀 Lean Bulk</span>
<span className="text-[10px] text-slate-400 block mt-0.5">+10% Surplus</span>
<span className="text-[9px] text-lime-400 block font-semibold">High Glycogen</span>
</button>
</div>
</div>
{/* Activity Level Selector */}
<div className="space-y-1.5 mt-4">
<label className="text-xs font-semibold text-slate-300">Weekly Training Frequency</label>
<div className="grid grid-cols-3 gap-2 text-center text-xs">
<button
type="button"
onClick={() => setMacroActivity('sedentary')}
className={`py-1.5 px-2 rounded-lg border text-xs font-medium transition-colors cursor-pointer ${
macroActivity === 'sedentary'
? 'bg-slate-800 text-white border-slate-600'
: 'bg-slate-950 text-slate-400 border-slate-800'
}`}
>
1-2 Days/Wk
</button>
<button
type="button"
onClick={() => setMacroActivity('moderate')}
className={`py-1.5 px-2 rounded-lg border text-xs font-medium transition-colors cursor-pointer ${
macroActivity === 'moderate'
? 'bg-slate-800 text-lime-300 border-lime-500/50'
: 'bg-slate-950 text-slate-400 border-slate-800'
}`}
>
3-4 Days/Wk
</button>
<button
type="button"
onClick={() => setMacroActivity('intense')}
className={`py-1.5 px-2 rounded-lg border text-xs font-medium transition-colors cursor-pointer ${
macroActivity === 'intense'
? 'bg-slate-800 text-lime-300 border-lime-500/50'
: 'bg-slate-950 text-slate-400 border-slate-800'
}`}
>
5-6 Days/Wk
</button>
</div>
</div>
{/* Calculated Targets Output */}
<div className="mt-4 p-4 rounded-2xl bg-slate-900 border border-slate-800 space-y-3">
<div className="flex items-baseline justify-between border-b border-slate-800/80 pb-2">
<span className="text-xs font-semibold text-slate-400 uppercase tracking-wider">Target Daily Calories</span>
<span className="text-2xl font-black text-white font-mono">{calcTargetCalories} <span className="text-sm font-bold text-lime-400">kcal</span></span>
</div>
<div className="grid grid-cols-3 gap-2 text-center pt-1">
<div className="p-2 rounded-lg bg-slate-950 border border-cyan-500/30">
<span className="text-[10px] text-cyan-400 font-bold uppercase block">Protein</span>
<span className="text-base font-extrabold text-white">{calcTargetProtein}g</span>
<span className="text-[9px] text-slate-500 block">{(calcTargetProtein / macroWeight).toFixed(1)} g/kg</span>
</div>
<div className="p-2 rounded-lg bg-slate-950 border border-amber-500/30">
<span className="text-[10px] text-amber-400 font-bold uppercase block">Carbs</span>
<span className="text-base font-extrabold text-white">{calcTargetCarbs}g</span>
<span className="text-[9px] text-slate-500 block">Glycogen</span>
</div>
<div className="p-2 rounded-lg bg-slate-950 border border-orange-500/30">
<span className="text-[10px] text-orange-400 font-bold uppercase block">Fats</span>
<span className="text-base font-extrabold text-white">{calcTargetFats}g</span>
<span className="text-[9px] text-slate-500 block">Hormones</span>
</div>
</div>
{/* Macro Distribution Bar */}
<div className="w-full h-2 rounded-full overflow-hidden flex bg-slate-950">
<div style={{ width: `${Math.round(((calcTargetProtein * 4) / calcTargetCalories) * 100)}%` }} className="bg-cyan-400 h-full" title="Protein" />
<div style={{ width: `${Math.round(((calcTargetCarbs * 4) / calcTargetCalories) * 100)}%` }} className="bg-amber-400 h-full" title="Carbs" />
<div style={{ width: `${Math.round(((calcTargetFats * 9) / calcTargetCalories) * 100)}%` }} className="bg-orange-400 h-full" title="Fats" />
</div>
</div>
{/* Sync Button */}
<button
type="button"
onClick={handleApplyMacroTargets}
className={`mt-4 w-full py-2.5 rounded-xl font-bold text-xs transition-all flex items-center justify-center gap-2 cursor-pointer shadow-lg ${
isSynced
? 'bg-emerald-500 text-slate-950 shadow-emerald-500/30'
: 'bg-gradient-to-r from-lime-500 to-emerald-500 text-slate-950 hover:scale-102 active:scale-98 shadow-lime-500/20'
}`}
>
{isSynced ? (
<>
<Check className="w-4 h-4 stroke-[3]" />
<span>Applied to Daily Fuel Station!</span>
</>
) : (
<>
<Target className="w-4 h-4" />
<span>Apply to Dashboard Fuel Station</span>
</>
)}
</button>
</div>
</div>
</div>
{/* Evidence-Based Science Principles */}
<div className="glass-card p-6 space-y-4">
<div className="flex items-center gap-2 border-b border-slate-800 pb-3">
<Info className="w-5 h-5 text-lime-400" />
<h3 className="font-bold text-slate-100 text-base">Evidence-Based Hypertrophy Principles</h3>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
{SCIENCE_TIPS.map((tip) => (
<div key={tip.title} className="p-4 rounded-xl bg-slate-900/60 border border-slate-800/80 space-y-1">
<div className="flex items-center justify-between">
<h4 className="text-xs font-bold text-lime-300">{tip.title}</h4>
<span className="text-[10px] px-2 py-0.2 rounded-full bg-slate-800 text-slate-400 font-semibold border border-slate-700">
{tip.concept}
</span>
</div>
<p className="text-xs text-slate-300 leading-relaxed pt-1">{tip.description}</p>
</div>
))}
</div>
</div>
</div>
{/* SECTION 4: PROGRESSIVE OVERLOAD WORKOUT LOG */}
<div className="glass-card overflow-hidden">
<div className="p-5 border-b border-slate-800 flex items-center justify-between">
<div>
<h3 className="text-base font-bold text-slate-100 flex items-center gap-2">
<TrendingUp className="w-5 h-5 text-lime-400" />
<span>Scientific Training Log & Volume Load</span>
</h3>
<p className="text-xs text-slate-400 mt-0.5">
Track Volume Load (Sets × Reps × Weight in kg) to ensure progressive overload across weeks.
</p>
</div>
<button
onClick={() => setIsWorkoutModalOpen(true)}
className="flex items-center gap-2 px-4 py-2 rounded-xl bg-gradient-to-r from-lime-500 to-emerald-500 text-slate-950 text-xs font-bold shadow-md shadow-lime-500/20 hover:scale-105 active:scale-95 transition-all cursor-pointer"
>
<Plus className="w-3.5 h-3.5" />
<span>New Session</span>
</button>
</div>
<div className="divide-y divide-slate-800/70">
{workouts.map((session) => (
<div key={session.id} className="p-6 space-y-4 hover:bg-slate-900/30 transition-colors group">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div>
<div className="flex items-center gap-3">
<h4 className="text-base font-bold text-white">{session.name}</h4>
<span className="text-xs text-slate-400 font-mono">[{session.date}]</span>
</div>
<div className="flex flex-wrap items-center gap-1.5 mt-1.5">
{session.muscleGroups.map((mg) => (
<span key={mg} className="text-[10px] px-2 py-0.5 rounded-full bg-slate-900 border border-slate-800 text-lime-400 font-semibold">
{mg}
</span>
))}
</div>
</div>
<div className="flex items-center gap-4">
<div className="text-right">
<span className="text-xs text-slate-400 block uppercase font-bold tracking-wider">Total Volume Load</span>
<span className="text-base font-extrabold text-lime-400 font-mono">
{session.volumeLoad.toLocaleString()} kg
</span>
</div>
<button
onClick={() => onDeleteWorkout(session.id)}
className="p-1.5 text-slate-500 hover:text-red-400 rounded transition-colors"
title="Delete workout log"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
{/* Exercise breakdown grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
{session.exercises.map((ex, idx) => (
<div key={idx} className="p-3.5 rounded-xl bg-slate-900/80 border border-slate-800 space-y-2">
<div className="flex items-center justify-between">
<span className="text-xs font-bold text-slate-200 truncate">{ex.name}</span>
<span className="text-[10px] text-slate-400 uppercase font-semibold">{ex.muscleGroup}</span>
</div>
<div className="space-y-1">
{ex.sets.map((s) => (
<div key={s.setNumber} className="flex items-center justify-between text-xs text-slate-400 bg-slate-950/60 px-2 py-1 rounded">
<span className="font-semibold text-slate-300">Set {s.setNumber}</span>
<span>{s.weight} kg × {s.reps} reps</span>
<span className={`text-[10px] font-bold px-1.5 py-0.2 rounded border ${
s.rpe >= 9 ? 'bg-red-500/10 text-red-400 border-red-500/30' : 'bg-lime-500/10 text-lime-400 border-lime-500/30'
}`}>
RPE {s.rpe}
</span>
</div>
))}
</div>
</div>
))}
</div>
{session.notes && (
<p className="text-xs text-slate-400 italic bg-slate-950/40 p-2.5 rounded-lg border border-slate-900">
💡 {session.notes}
</p>
)}
</div>
))}
{workouts.length === 0 && (
<div className="py-12 text-center text-slate-500 text-xs">
No workouts logged yet. Start recording your sessions to build a progressive overload history!
</div>
)}
</div>
</div>
{/* MODAL: LOG SCIENTIFIC WORKOUT */}
{isWorkoutModalOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/70 backdrop-blur-sm animate-in fade-in duration-200">
<div className="relative w-full max-w-2xl rounded-2xl bg-[#0e1424] border border-lime-500/30 p-6 shadow-2xl space-y-4 max-h-[90vh] overflow-y-auto">
<div className="flex items-center justify-between pb-3 border-b border-slate-800">
<h3 className="text-base font-bold text-white flex items-center gap-2">
<Dumbbell className="w-4 h-4 text-lime-400" />
Log Scientific Workout Session
</h3>
<button
onClick={() => setIsWorkoutModalOpen(false)}
className="text-slate-400 hover:text-white p-1 rounded-lg hover:bg-slate-800"
>
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleCreateWorkout} className="space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div className="sm:col-span-2">
<label className="block text-xs font-semibold text-slate-300 mb-1">Session Name *</label>
<input
type="text"
required
placeholder="e.g. Push Hypertrophy, Legs & Core"
value={workoutName}
onChange={(e) => setWorkoutName(e.target.value)}
className="w-full px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
</div>
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">Date</label>
<input
type="date"
value={workoutDate}
onChange={(e) => setWorkoutDate(e.target.value)}
className="w-full px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
</div>
</div>
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">Target Muscle Groups (comma separated)</label>
<input
type="text"
placeholder="Chest, Shoulders, Triceps"
value={workoutMuscles}
onChange={(e) => setWorkoutMuscles(e.target.value)}
className="w-full px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
</div>
{/* Dynamic Exercise Rows */}
<div className="space-y-3 pt-2">
<div className="flex items-center justify-between">
<label className="text-xs font-bold text-lime-400 uppercase tracking-wider">Exercise List</label>
<button
type="button"
onClick={addExerciseRow}
className="text-xs text-lime-400 hover:text-lime-300 font-semibold flex items-center gap-1"
>
<Plus className="w-3.5 h-3.5" /> Add Exercise
</button>
</div>
{exerciseList.map((ex, idx) => (
<div key={idx} className="p-3.5 rounded-xl bg-slate-900 border border-slate-800 space-y-2 relative">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
<input
type="text"
required
placeholder="Exercise Name (e.g. Incline DB Press)"
value={ex.name}
onChange={(e) => {
const updated = [...exerciseList];
updated[idx].name = e.target.value;
setExerciseList(updated);
}}
className="px-3 py-1.5 rounded-lg bg-slate-950 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
<input
type="text"
placeholder="Muscle Group (e.g. Chest)"
value={ex.muscleGroup}
onChange={(e) => {
const updated = [...exerciseList];
updated[idx].muscleGroup = e.target.value;
setExerciseList(updated);
}}
className="px-3 py-1.5 rounded-lg bg-slate-950 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
</div>
<div className="grid grid-cols-4 gap-2">
<div>
<label className="text-[10px] text-slate-400">Sets</label>
<input
type="number"
min="1"
max="10"
value={ex.setsCount}
onChange={(e) => {
const updated = [...exerciseList];
updated[idx].setsCount = Number(e.target.value);
setExerciseList(updated);
}}
className="w-full px-2 py-1 rounded bg-slate-950 border border-slate-800 text-xs text-slate-200 text-center"
/>
</div>
<div>
<label className="text-[10px] text-slate-400">Weight (kg)</label>
<input
type="number"
step="0.5"
value={ex.weight}
onChange={(e) => {
const updated = [...exerciseList];
updated[idx].weight = Number(e.target.value);
setExerciseList(updated);
}}
className="w-full px-2 py-1 rounded bg-slate-950 border border-slate-800 text-xs text-slate-200 text-center"
/>
</div>
<div>
<label className="text-[10px] text-slate-400">Reps</label>
<input
type="number"
min="1"
value={ex.reps}
onChange={(e) => {
const updated = [...exerciseList];
updated[idx].reps = Number(e.target.value);
setExerciseList(updated);
}}
className="w-full px-2 py-1 rounded bg-slate-950 border border-slate-800 text-xs text-slate-200 text-center"
/>
</div>
<div>
<label className="text-[10px] text-slate-400">RPE (6-10)</label>
<input
type="number"
step="0.5"
min="6"
max="10"
value={ex.rpe}
onChange={(e) => {
const updated = [...exerciseList];
updated[idx].rpe = Number(e.target.value);
setExerciseList(updated);
}}
className="w-full px-2 py-1 rounded bg-slate-950 border border-slate-800 text-xs text-slate-200 text-center"
/>
</div>
</div>
{exerciseList.length > 1 && (
<button
type="button"
onClick={() => removeExerciseRow(idx)}
className="text-[11px] text-red-400 hover:text-red-300 pt-1"
>
Remove Exercise
</button>
)}
</div>
))}
</div>
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">Scientific Notes & Biomechanics</label>
<textarea
rows={2}
placeholder="Tempo, lengthened position stretch, fatigue level..."
value={workoutNotes}
onChange={(e) => setWorkoutNotes(e.target.value)}
className="w-full px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-slate-200 resize-none focus:outline-none focus:border-lime-500"
/>
</div>
<div className="pt-3 border-t border-slate-800 flex justify-end gap-3">
<button
type="button"
onClick={() => setIsWorkoutModalOpen(false)}
className="px-4 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs font-semibold text-slate-300 transition-colors"
>
Cancel
</button>
<button
type="submit"
className="px-5 py-2 rounded-xl bg-gradient-to-r from-lime-500 to-emerald-500 text-slate-950 text-xs font-bold shadow-md shadow-lime-500/20 hover:scale-105 active:scale-95 transition-all cursor-pointer"
>
Save Workout Session
</button>
</div>
</form>
</div>
</div>
)}
{/* MODAL: ADJUST MACROS & BODYWEIGHT */}
{isNutritionModalOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/70 backdrop-blur-sm animate-in fade-in duration-200">
<div className="relative w-full max-w-md rounded-2xl bg-[#0e1424] border border-lime-500/30 p-6 shadow-2xl space-y-4">
<div className="flex items-center justify-between pb-3 border-b border-slate-800">
<h3 className="text-base font-bold text-white flex items-center gap-2">
<Scale className="w-4 h-4 text-lime-400" />
Adjust Daily Macros & Weight
</h3>
<button
onClick={() => setIsNutritionModalOpen(false)}
className="text-slate-400 hover:text-white p-1 rounded-lg hover:bg-slate-800"
>
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleSaveNutrition} className="space-y-4">
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">Bodyweight (kg)</label>
<input
type="number"
step="0.1"
required
value={editWeight}
onChange={(e) => setEditWeight(Number(e.target.value))}
className="w-full px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">Calories (kcal)</label>
<input
type="number"
required
value={editCalories}
onChange={(e) => setEditCalories(Number(e.target.value))}
className="w-full px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
</div>
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">Protein (g)</label>
<input
type="number"
required
value={editProtein}
onChange={(e) => setEditProtein(Number(e.target.value))}
className="w-full px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">Carbs (g)</label>
<input
type="number"
required
value={editCarbs}
onChange={(e) => setEditCarbs(Number(e.target.value))}
className="w-full px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
</div>
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">Fats (g)</label>
<input
type="number"
required
value={editFats}
onChange={(e) => setEditFats(Number(e.target.value))}
className="w-full px-3 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-slate-200 focus:outline-none focus:border-lime-500"
/>
</div>
</div>
<div className="pt-3 border-t border-slate-800 flex justify-end gap-3">
<button
type="button"
onClick={() => setIsNutritionModalOpen(false)}
className="px-4 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs font-semibold text-slate-300 transition-colors"
>
Cancel
</button>
<button
type="submit"
className="px-5 py-2 rounded-xl bg-gradient-to-r from-lime-500 to-emerald-500 text-slate-950 text-xs font-bold shadow-md shadow-lime-500/20 hover:scale-105 active:scale-95 transition-all cursor-pointer"
>
Save Targets
</button>
</div>
</form>
</div>
</div>
)}
</div>
);
};