Merge version_2 into main #10

Merged
bender merged 7 commits from version_2 into main 2026-03-11 19:50:29 +00:00
6 changed files with 1006 additions and 859 deletions

View File

@@ -0,0 +1,64 @@
'use client';
import React, { useCallback } from 'react';
import { useWorkoutTracking } from '@/app/hooks/useWorkoutTracking';
import { WorkoutSession, CardioSession, NutritionLog } from '@/app/lib/storage/workoutStorage';
export interface WorkoutDataIntegrationProps {
onSave?: (data: any) => void;
autoSave?: boolean;
}
/**
* WorkoutDataIntegration component that provides workout tracking context
* Use this component to wrap sections that need to save workout data
*/
export const WorkoutDataIntegration: React.FC<{
children: React.ReactNode;
} & WorkoutDataIntegrationProps> = ({ children, onSave, autoSave = true }) => {
const { metrics, addWorkoutSession, addCardioSession, addNutritionLog, refreshMetrics } =
useWorkoutTracking();
const handleWorkoutSave = useCallback(
(session: WorkoutSession) => {
addWorkoutSession(session);
if (onSave) onSave({ type: 'workout', data: session });
},
[addWorkoutSession, onSave]
);
const handleCardioSave = useCallback(
(session: CardioSession) => {
addCardioSession(session);
if (onSave) onSave({ type: 'cardio', data: session });
},
[addCardioSession, onSave]
);
const handleNutritionSave = useCallback(
(log: NutritionLog) => {
addNutritionLog(log);
if (onSave) onSave({ type: 'nutrition', data: log });
},
[addNutritionLog, onSave]
);
// Expose save functions via context or props
const contextValue = {
metrics,
handleWorkoutSave,
handleCardioSave,
handleNutritionSave,
refreshMetrics,
autoSave,
};
// Clone children and pass context data as props
return (
<div data-workout-integration="true" data-context={JSON.stringify(contextValue)}>
{children}
</div>
);
};
export default WorkoutDataIntegration;

View File

@@ -0,0 +1,56 @@
'use client';
import { useState, useCallback } from 'react';
import {
saveWorkoutSession,
saveCardioSession,
saveNutritionLog,
getUserMetrics,
getWorkoutSessions,
getCardioSessions,
getNutritionLogs,
WorkoutSession,
CardioSession,
NutritionLog,
UserMetrics,
} from '@/app/lib/storage/workoutStorage';
export const useWorkoutTracking = () => {
const [metrics, setMetrics] = useState<UserMetrics>(getUserMetrics());
const [workouts, setWorkouts] = useState<WorkoutSession[]>(getWorkoutSessions());
const [cardioSessions, setCardioSessions] = useState<CardioSession[]>(getCardioSessions());
const [nutritionLogs, setNutritionLogs] = useState<NutritionLog[]>(getNutritionLogs());
const addWorkoutSession = useCallback((session: WorkoutSession) => {
saveWorkoutSession(session);
setWorkouts(getWorkoutSessions());
setMetrics(getUserMetrics());
}, []);
const addCardioSession = useCallback((session: CardioSession) => {
saveCardioSession(session);
setCardioSessions(getCardioSessions());
setMetrics(getUserMetrics());
}, []);
const addNutritionLog = useCallback((log: NutritionLog) => {
saveNutritionLog(log);
setNutritionLogs(getNutritionLogs());
setMetrics(getUserMetrics());
}, []);
const refreshMetrics = useCallback(() => {
setMetrics(getUserMetrics());
}, []);
return {
metrics,
workouts,
cardioSessions,
nutritionLogs,
addWorkoutSession,
addCardioSession,
addNutritionLog,
refreshMetrics,
};
};

View File

@@ -0,0 +1,309 @@
/**
* Workout and metrics data persistence layer
* Handles saving and retrieving workout data from localStorage
*/
export interface WorkoutSet {
reps: number;
weight: number;
timestamp: number;
}
export interface WorkoutSession {
id: string;
exerciseName: string;
date: string;
sets: WorkoutSet[];
duration: number; // in seconds
caloriesBurned: number;
notes?: string;
}
export interface CardioSession {
id: string;
type: 'running' | 'walking' | 'cycling';
date: string;
distance: number; // in km
duration: number; // in seconds
caloriesBurned: number;
pace: number; // km/h
steps?: number;
route?: string;
}
export interface NutritionLog {
id: string;
date: string;
calories: number;
protein: number;
carbs: number;
fats: number;
meals: string[];
}
export interface UserMetrics {
totalWorkouts: number;
totalCardioDistance: number;
totalCaloriesBurned: number;
currentStreak: number;
personalRecords: Record<string, number>;
lastUpdated: number;
}
const STORAGE_KEYS = {
WORKOUT_SESSIONS: 'fitflow_workout_sessions',
CARDIO_SESSIONS: 'fitflow_cardio_sessions',
NUTRITION_LOGS: 'fitflow_nutrition_logs',
USER_METRICS: 'fitflow_user_metrics',
};
/**
* Save a workout session to localStorage
*/
export const saveWorkoutSession = (session: WorkoutSession): void => {
if (typeof window === 'undefined') return;
try {
const sessions = getWorkoutSessions();
sessions.push(session);
localStorage.setItem(STORAGE_KEYS.WORKOUT_SESSIONS, JSON.stringify(sessions));
updateUserMetrics();
} catch (error) {
console.error('Error saving workout session:', error);
}
};
/**
* Get all workout sessions from localStorage
*/
export const getWorkoutSessions = (): WorkoutSession[] => {
if (typeof window === 'undefined') return [];
try {
const data = localStorage.getItem(STORAGE_KEYS.WORKOUT_SESSIONS);
return data ? JSON.parse(data) : [];
} catch (error) {
console.error('Error retrieving workout sessions:', error);
return [];
}
};
/**
* Save a cardio session to localStorage
*/
export const saveCardioSession = (session: CardioSession): void => {
if (typeof window === 'undefined') return;
try {
const sessions = getCardioSessions();
sessions.push(session);
localStorage.setItem(STORAGE_KEYS.CARDIO_SESSIONS, JSON.stringify(sessions));
updateUserMetrics();
} catch (error) {
console.error('Error saving cardio session:', error);
}
};
/**
* Get all cardio sessions from localStorage
*/
export const getCardioSessions = (): CardioSession[] => {
if (typeof window === 'undefined') return [];
try {
const data = localStorage.getItem(STORAGE_KEYS.CARDIO_SESSIONS);
return data ? JSON.parse(data) : [];
} catch (error) {
console.error('Error retrieving cardio sessions:', error);
return [];
}
};
/**
* Save a nutrition log to localStorage
*/
export const saveNutritionLog = (log: NutritionLog): void => {
if (typeof window === 'undefined') return;
try {
const logs = getNutritionLogs();
logs.push(log);
localStorage.setItem(STORAGE_KEYS.NUTRITION_LOGS, JSON.stringify(logs));
updateUserMetrics();
} catch (error) {
console.error('Error saving nutrition log:', error);
}
};
/**
* Get all nutrition logs from localStorage
*/
export const getNutritionLogs = (): NutritionLog[] => {
if (typeof window === 'undefined') return [];
try {
const data = localStorage.getItem(STORAGE_KEYS.NUTRITION_LOGS);
return data ? JSON.parse(data) : [];
} catch (error) {
console.error('Error retrieving nutrition logs:', error);
return [];
}
};
/**
* Get user metrics from localStorage
*/
export const getUserMetrics = (): UserMetrics => {
if (typeof window === 'undefined') {
return {
totalWorkouts: 0,
totalCardioDistance: 0,
totalCaloriesBurned: 0,
currentStreak: 0,
personalRecords: {},
lastUpdated: Date.now(),
};
}
try {
const data = localStorage.getItem(STORAGE_KEYS.USER_METRICS);
return data
? JSON.parse(data)
: {
totalWorkouts: 0,
totalCardioDistance: 0,
totalCaloriesBurned: 0,
currentStreak: 0,
personalRecords: {},
lastUpdated: Date.now(),
};
} catch (error) {
console.error('Error retrieving user metrics:', error);
return {
totalWorkouts: 0,
totalCardioDistance: 0,
totalCaloriesBurned: 0,
currentStreak: 0,
personalRecords: {},
lastUpdated: Date.now(),
};
}
};
/**
* Update user metrics based on saved sessions
*/
export const updateUserMetrics = (): void => {
if (typeof window === 'undefined') return;
try {
const workoutSessions = getWorkoutSessions();
const cardioSessions = getCardioSessions();
const nutritionLogs = getNutritionLogs();
let totalWorkouts = workoutSessions.length;
let totalCardioDistance = cardioSessions.reduce((sum, session) => sum + session.distance, 0);
let totalCaloriesBurned = workoutSessions.reduce((sum, session) => sum + session.caloriesBurned, 0)
+ cardioSessions.reduce((sum, session) => sum + session.caloriesBurned, 0);
// Calculate streak (consecutive days with activity)
const currentStreak = calculateStreak([...workoutSessions, ...cardioSessions]);
// Extract personal records from workouts
const personalRecords: Record<string, number> = {};
workoutSessions.forEach((session) => {
const maxWeight = Math.max(...session.sets.map((s) => s.weight));
const key = `${session.exerciseName}_pr`;
if (!personalRecords[key] || maxWeight > personalRecords[key]) {
personalRecords[key] = maxWeight;
}
});
const metrics: UserMetrics = {
totalWorkouts,
totalCardioDistance,
totalCaloriesBurned,
currentStreak,
personalRecords,
lastUpdated: Date.now(),
};
localStorage.setItem(STORAGE_KEYS.USER_METRICS, JSON.stringify(metrics));
} catch (error) {
console.error('Error updating user metrics:', error);
}
};
/**
* Calculate consecutive days with activity
*/
const calculateStreak = (sessions: Array<WorkoutSession | CardioSession>): number => {
if (sessions.length === 0) return 0;
const dates = sessions.map((session) => new Date(session.date).toDateString()).filter((date, index, self) => self.indexOf(date) === index).sort((a, b) => new Date(b).getTime() - new Date(a).getTime());
let streak = 1;
for (let i = 1; i < dates.length; i++) {
const currentDate = new Date(dates[i]);
const previousDate = new Date(dates[i - 1]);
const diffTime = Math.abs(previousDate.getTime() - currentDate.getTime());
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 1) {
streak++;
} else {
break;
}
}
return streak;
};
/**
* Clear all stored data
*/
export const clearAllData = (): void => {
if (typeof window === 'undefined') return;
try {
localStorage.removeItem(STORAGE_KEYS.WORKOUT_SESSIONS);
localStorage.removeItem(STORAGE_KEYS.CARDIO_SESSIONS);
localStorage.removeItem(STORAGE_KEYS.NUTRITION_LOGS);
localStorage.removeItem(STORAGE_KEYS.USER_METRICS);
} catch (error) {
console.error('Error clearing data:', error);
}
};
/**
* Export data as JSON for backup
*/
export const exportData = (): string => {
const data = {
workouts: getWorkoutSessions(),
cardio: getCardioSessions(),
nutrition: getNutritionLogs(),
metrics: getUserMetrics(),
exportDate: new Date().toISOString(),
};
return JSON.stringify(data, null, 2);
};
/**
* Import data from JSON backup
*/
export const importData = (jsonData: string): boolean => {
if (typeof window === 'undefined') return false;
try {
const data = JSON.parse(jsonData);
if (data.workouts) localStorage.setItem(STORAGE_KEYS.WORKOUT_SESSIONS, JSON.stringify(data.workouts));
if (data.cardio) localStorage.setItem(STORAGE_KEYS.CARDIO_SESSIONS, JSON.stringify(data.cardio));
if (data.nutrition) localStorage.setItem(STORAGE_KEYS.NUTRITION_LOGS, JSON.stringify(data.nutrition));
if (data.metrics) localStorage.setItem(STORAGE_KEYS.USER_METRICS, JSON.stringify(data.metrics));
return true;
} catch (error) {
console.error('Error importing data:', error);
return false;
}
};

View File

@@ -2,50 +2,46 @@
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
import { useState } from 'react';
import { Mail, Lock, Eye, EyeOff, ArrowRight } from 'lucide-react';
import { useState } from "react";
import { Eye, EyeOff, Mail, Lock } from 'lucide-react';
import Input from '@/components/form/Input';
export default function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [errors, setErrors] = useState<{ email?: string; password?: string }>({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSubmitted, setIsSubmitted] = useState(false);
const validateForm = () => {
const newErrors: { email?: string; password?: string } = {};
if (!email) {
newErrors.email = 'Email is required';
newErrors.email = "Email é obrigatório";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
newErrors.email = 'Please enter a valid email';
newErrors.email = "Email inválido";
}
if (!password) {
newErrors.password = 'Password is required';
newErrors.password = "Senha é obrigatória";
} else if (password.length < 6) {
newErrors.password = 'Password must be at least 6 characters';
newErrors.password = "Senha deve ter no mínimo 6 caracteres";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
return newErrors;
};
const handleSubmit = async (e: React.FormEvent) => {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const newErrors = validateForm();
setErrors(newErrors);
if (!validateForm()) {
return;
}
setIsSubmitting(true);
try {
// Simulated API call
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Login attempt:', { email, password });
// In a real app, you would redirect to dashboard or handle authentication
} finally {
setIsSubmitting(false);
if (Object.keys(newErrors).length === 0) {
setIsSubmitted(true);
console.log("Login attempt:", { email, password });
setTimeout(() => {
setIsSubmitted(false);
}, 2000);
}
};
@@ -66,170 +62,99 @@ export default function LoginPage() {
<NavbarStyleCentered
navItems={[
{ name: "Dashboard", id: "/" },
{ name: "Treino", id: "#training" },
{ name: "Nutrição", id: "#nutrition" },
{ name: "Comunidade", id: "#community" },
{ name: "Perfil", id: "#profile" }
{ name: "Treino", id: "training" },
{ name: "Nutrição", id: "nutrition" },
{ name: "Comunidade", id: "community" },
{ name: "Perfil", id: "profile" }
]}
button={{ text: "Começar Agora", href: "/signup" }}
brandName="FitFlow Pro"
/>
</div>
<div id="login" data-section="login" className="min-h-screen flex items-center justify-center px-4 py-20">
<div className="min-h-[calc(100vh-80px)] flex items-center justify-center py-12 px-4">
<div className="w-full max-w-md">
<div className="rounded-2xl border border-opacity-20 p-8 backdrop-blur-sm" style={{
backgroundColor: 'var(--color-card)',
borderColor: 'var(--color-foreground)'
}}>
{/* Header */}
<div className="mb-8 text-center">
<h1 className="text-3xl font-bold mb-2" style={{ color: 'var(--color-foreground)' }}>
Welcome Back
</h1>
<p className="text-sm" style={{ color: 'var(--color-foreground)', opacity: 0.7 }}>
Sign in to your FitFlow Pro account
</p>
<div className="bg-card rounded-3xl shadow-lg p-8 border border-accent/10">
<div className="mb-8">
<h1 className="text-3xl font-extrabold text-foreground mb-2">Bem-vindo de Volta</h1>
<p className="text-foreground/70">Faça login para acessar seu progresso</p>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="space-y-6">
{/* Email Field */}
<div>
<label htmlFor="email" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Email Address
<label className="block text-sm font-medium text-foreground mb-2">
<div className="flex items-center gap-2">
<Mail size={16} />
Email
</div>
</label>
<div className="relative">
<Mail className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com"
className="w-full pl-10 pr-4 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.email ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
/>
</div>
<Input
value={email}
onChange={setEmail}
type="email"
placeholder="seu@email.com"
required
className={errors.email ? "border-red-500" : ""}
/>
{errors.email && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
{errors.email}
</p>
<p className="text-red-500 text-sm mt-1">{errors.email}</p>
)}
</div>
{/* Password Field */}
<div>
<label htmlFor="password" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Password
<label className="block text-sm font-medium text-foreground mb-2">
<div className="flex items-center gap-2">
<Lock size={16} />
Senha
</div>
</label>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="password"
type={showPassword ? 'text' : 'password'}
<Input
value={password}
onChange={(e) => setPassword(e.target.value)}
onChange={setPassword}
type={showPassword ? "text" : "password"}
placeholder="••••••••"
className="w-full pl-10 pr-10 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.password ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
required
className={errors.password ? "border-red-500" : "pr-10"}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-3.5 transition-opacity hover:opacity-75"
className="absolute right-3 top-1/2 -translate-y-1/2 text-foreground/50 hover:text-foreground transition-colors"
aria-label={showPassword ? "Ocultar senha" : "Mostrar senha"}
>
{showPassword ? (
<EyeOff className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
) : (
<Eye className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
)}
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
{errors.password && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
{errors.password}
</p>
<p className="text-red-500 text-sm mt-1">{errors.password}</p>
)}
</div>
{/* Remember & Forgot */}
<div className="flex items-center justify-between">
<label className="flex items-center cursor-pointer">
<input type="checkbox" className="w-4 h-4 rounded" style={{ accentColor: 'var(--color-primary-cta)' }} />
<span className="ml-2 text-sm" style={{ color: 'var(--color-foreground)', opacity: 0.7 }}>Remember me</span>
</label>
<a href="#forgot" className="text-sm font-medium hover:underline" style={{ color: 'var(--color-primary-cta)' }}>
Forgot password?
</a>
</div>
{/* Submit Button */}
<button
type="submit"
disabled={isSubmitting}
className="w-full py-2.5 rounded-lg font-semibold transition-all flex items-center justify-center gap-2 disabled:opacity-50"
style={{
backgroundColor: 'var(--color-primary-cta)',
color: 'white',
}}
disabled={isSubmitted}
className="w-full py-3 px-4 bg-primary-cta hover:opacity-90 disabled:opacity-50 text-white font-semibold rounded-full transition-all duration-300 transform hover:scale-105"
>
{isSubmitting ? 'Signing in...' : 'Sign In'}
{!isSubmitting && <ArrowRight className="w-4 h-4" />}
{isSubmitted ? "Entrando..." : "Entrar"}
</button>
</form>
{/* Divider */}
<div className="my-6 flex items-center gap-4">
<div className="flex-1 h-px" style={{ backgroundColor: 'var(--color-foreground)', opacity: 0.1 }} />
<span className="text-xs" style={{ color: 'var(--color-foreground)', opacity: 0.5 }}>OR</span>
<div className="flex-1 h-px" style={{ backgroundColor: 'var(--color-foreground)', opacity: 0.1 }} />
<div className="mt-6 text-center text-sm text-foreground/70">
<p>
Não tem conta?{" "}
<a href="/signup" className="text-primary-cta font-semibold hover:underline">
Criar conta
</a>
</p>
</div>
{/* Social Login */}
<div className="grid grid-cols-2 gap-3">
<button
type="button"
className="py-2.5 rounded-lg border font-medium transition-all hover:opacity-80"
style={{
backgroundColor: 'var(--color-background)',
borderColor: 'var(--color-foreground)',
color: 'var(--color-foreground)',
}}
>
Google
</button>
<button
type="button"
className="py-2.5 rounded-lg border font-medium transition-all hover:opacity-80"
style={{
backgroundColor: 'var(--color-background)',
borderColor: 'var(--color-foreground)',
color: 'var(--color-foreground)',
}}
>
Apple
</button>
<div className="mt-6 pt-6 border-t border-accent/10 text-center text-xs text-foreground/50">
<p>Teste gratuito por 30 dias. Sem cartão de crédito necessário.</p>
</div>
{/* Sign Up Link */}
<p className="mt-6 text-center text-sm" style={{ color: 'var(--color-foreground)', opacity: 0.7 }}>
Don't have an account?{' '}
<a href="/signup" className="font-semibold hover:underline" style={{ color: 'var(--color-primary-cta)' }}>
Sign up
</a>
</p>
</div>
</div>
</div>
</ThemeProvider>
);
}
}

View File

@@ -1,7 +1,6 @@
'use client';
"use client";
import React from 'react';
import { ThemeProvider } from '@/providers/themeProvider/ThemeProvider';
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
import HeroSplitKpi from '@/components/sections/hero/HeroSplitKpi';
import FeatureCardTwentyFive from '@/components/sections/feature/FeatureCardTwentyFive';
@@ -13,450 +12,375 @@ import TestimonialCardTwelve from '@/components/sections/testimonial/Testimonial
import SocialProofOne from '@/components/sections/socialProof/SocialProofOne';
import ContactText from '@/components/sections/contact/ContactText';
import FooterLogoEmphasis from '@/components/sections/footer/FooterLogoEmphasis';
import { Zap, Dumbbell, Apple, TrendingUp } from 'lucide-react';
import { Activity, Apple, Brain, Dumbbell, Heart, Target, Zap, Users, Star, TrendingDown, TrendingUp } from 'lucide-react';
import { WorkoutDataIntegration } from '@/app/components/WorkoutDataIntegration';
const navItems = [
{ name: 'Home', id: '/' },
{ name: 'Features', id: 'onboarding' },
{ name: 'Pricing', id: 'nutrition' },
{ name: 'Team', id: 'team' },
{ name: 'Contact', id: 'contact' },
];
const Page = () => {
export default function LandingPage() {
return (
<ThemeProvider
defaultButtonVariant="text-stagger"
defaultTextAnimation="entrance-slide"
borderRadius="rounded"
contentWidth="medium"
sizing="medium"
background="circleGradient"
cardStyle="glass-elevated"
primaryButtonStyle="gradient"
secondaryButtonStyle="glass"
headingFontWeight="normal"
>
<div id="nav" data-section="nav">
<NavbarStyleCentered
navItems={navItems}
brandName="FitFlow"
/>
</div>
<WorkoutDataIntegration autoSave={true}>
<ThemeProvider
defaultButtonVariant="elastic-effect"
defaultTextAnimation="entrance-slide"
borderRadius="pill"
contentWidth="smallMedium"
sizing="mediumSizeLargeTitles"
background="blurBottom"
cardStyle="gradient-bordered"
primaryButtonStyle="flat"
secondaryButtonStyle="glass"
headingFontWeight="extrabold"
>
<div id="nav" data-section="nav">
<NavbarStyleCentered
navItems={[
{ name: "Dashboard", id: "dashboard" },
{ name: "Treino", id: "training" },
{ name: "Nutrição", id: "nutrition" },
{ name: "Comunidade", id: "community" },
{ name: "Perfil", id: "profile" }
]}
button={{ text: "Começar Agora", href: "contact" }}
brandName="FitFlow Pro"
/>
</div>
<div id="hero" data-section="hero">
<HeroSplitKpi
title="Transform Your Fitness Journey"
description="Track workouts, monitor nutrition, and achieve your goals with our comprehensive fitness platform."
background={{ variant: 'circleGradient' }}
tag="Fitness Platform"
tagIcon={Zap}
tagAnimation="slide-up"
kpis={[
{ value: '500K+', label: 'Active Users' },
{ value: '1M+', label: 'Workouts Tracked' },
{ value: '50+', label: 'Fitness Experts' }
]}
enableKpiAnimation={true}
buttons={[
{ text: 'Start Free Trial', href: '/signup' },
{ text: 'Learn More', href: '#features' }
]}
buttonAnimation="slide-up"
mediaAnimation="slide-up"
imageSrc="/placeholders/placeholder1.webp"
imageAlt="Fitness tracking dashboard"
imagePosition="right"
/>
</div>
<div id="hero" data-section="hero">
<HeroSplitKpi
title="Seu Corpo, Seu Histórico, Sua Vitória"
description="Aplicativo premium de fitness com rastreamento biométrico inteligente, treinos personalizados por IA e cardio com GPS. Transforme seu corpo enquanto acompanha cada segundo da jornada."
tag="Fitness Ultra-Premium"
tagIcon={Zap}
background={{ variant: "glowing-orb" }}
kpis={[
{ value: "150K+", label: "Usuários Ativos" },
{ value: "2M+", label: "Treinos Concluídos" },
{ value: "4.9★", label: "Avaliação" }
]}
enableKpiAnimation={true}
imageSrc="https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/ultra-modern-fitness-app-dashboard-with--1773256981295-f56c580b.png?_wi=1"
imageAlt="Dashboard de fitness premium"
imagePosition="right"
mediaAnimation="slide-up"
buttons={[
{ text: "Começar Teste Grátis", href: "contact" },
{ text: "Ver Demo", href: "#features" }
]}
avatars={[
{ src: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/professional-athlete-portrait-male-fitne-1773256979726-5009f852.png", alt: "Usuário" },
{ src: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/fit-female-athlete-portrait-determined-e-1773256980310-c05dce2f.png", alt: "Usuário" },
{ src: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/athletic-male-trainer-portrait-confident-1773256979906-c5e05a88.png", alt: "Usuário" }
]}
avatarText="Join 150K+ athletes"
/>
</div>
<div id="onboarding" data-section="onboarding">
<FeatureCardTwentyFive
title="Get Started in 3 Simple Steps"
description="Onboard quickly and start your fitness transformation today."
tag="Onboarding"
tagIcon={Zap}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
features={[
{
title: 'Create Your Profile',
description: 'Set up your account with your fitness goals.',
icon: Zap,
mediaItems: [
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Profile setup' },
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Goals selection' }
]
},
{
title: 'Connect Your Devices',
description: 'Sync with your fitness trackers and smartwatches.',
icon: Dumbbell,
mediaItems: [
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Device sync' },
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Integration' }
]
},
{
title: 'Start Tracking',
description: 'Log workouts and monitor your progress in real-time.',
icon: TrendingUp,
mediaItems: [
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Workout tracking' },
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Progress dashboard' }
]
}
]}
buttons={[
{ text: 'Get Started Now', href: '/signup' }
]}
buttonAnimation="slide-up"
/>
</div>
<div id="onboarding" data-section="onboarding">
<FeatureCardTwentyFive
title="Onboarding Biométrico Dinâmico"
description="Sistema inteligente que adapta-se ao seu corpo. Defina seu objetivo, e o app calcula automaticamente sua timeline de sucesso com precisão científica."
tag="Inteligência Adaptativa"
tagIcon={Brain}
features={[
{
title: "Perfil Biométrico", description: "Coloque seus dados (gênero, altura, peso, idade) e veja o app adaptar todas as demonstrações, avatares e ilustrações anatomicamente.", icon: Target,
mediaItems: [
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/premium-onboarding-screen-for-fitness-ap-1773256981180-774b293c.png", imageAlt: "Tela de biometria"
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/ultra-modern-fitness-app-dashboard-with--1773256981295-f56c580b.png?_wi=2", imageAlt: "Dashboard adaptado"
}
]
},
{
title: "Metas Inteligentes", description: "Escolha: Perder Peso ou Ganhar Massa. O sistema calcula TMB, projeta planilha de metas e mostra exatamente quando você atingirá seu objetivo.", icon: Zap,
mediaItems: [
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/nutrition-dashboard-showing-meal-plans-d-1773256981349-9348b6d9.png", imageAlt: "Plano de nutrição"
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/performance-metrics-showcase-displaying--1773256982260-f9a5cff0.png?_wi=1", imageAlt: "Métricas de progresso"
}
]
}
]}
animationType="blur-reveal"
textboxLayout="default"
useInvertedBackground={false}
/>
</div>
<div id="cardio" data-section="cardio">
<FeatureCardTwentyFive
title="Cardio Workouts"
description="High-intensity interval training and endurance workouts."
tag="Cardio"
tagIcon={Zap}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
features={[
{
title: 'HIIT Training',
description: 'Burn calories with high-intensity interval training.',
icon: Zap,
mediaItems: [
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'HIIT workout' },
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'HIIT results' }
]
},
{
title: 'Running Plans',
description: 'Personalized running programs for all levels.',
icon: TrendingUp,
mediaItems: [
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Running plan' },
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Running stats' }
]
},
{
title: 'Cycling Routes',
description: 'Discover and track cycling routes in your area.',
icon: Dumbbell,
mediaItems: [
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Cycling route' },
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Route map' }
]
}
]}
buttons={[
{ text: 'Explore Cardio', href: '#workouts' }
]}
buttonAnimation="slide-up"
/>
</div>
<div id="cardio" data-section="cardio">
<FeatureCardTwentyFive
title="Hub de Cardio com GPS em Tempo Real"
description="Rastreie suas corridas e caminhadas com precisão de GPS. Monitore pace, distância, calorias queimadas e vença suas metas diárias com anéis de progresso animados."
tag="Cardio Premium"
tagIcon={Heart}
features={[
{
title: "Running Tracker Avançado", description: "GPS ativado em tempo real. Mapeia sua rota, calcula queima de calorias baseado no peso, mostra ritmo ao vivo em painel estilo smartwatch.", icon: Zap,
mediaItems: [
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/gps-running-tracker-interface-with-real--1773256980694-2abe167e.png", imageAlt: "Rastreamento GPS"
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/performance-metrics-showcase-displaying--1773256982260-f9a5cff0.png?_wi=2", imageAlt: "Métricas de cardio"
}
]
},
{
title: "Pedômetro & Caminhada", description: "Contador de passos embutido com rastreamento de distância e calorias. Vença suas metas de 10.000 passos com visual de anéis de progresso motivacional.", icon: Activity,
mediaItems: [
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/ultra-modern-fitness-app-dashboard-with--1773256981295-f56c580b.png?_wi=3", imageAlt: "Dashboard de passos"
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/performance-metrics-showcase-displaying--1773256982260-f9a5cff0.png?_wi=3", imageAlt: "Progresso de atividade"
}
]
}
]}
animationType="depth-3d"
textboxLayout="default"
useInvertedBackground={false}
/>
</div>
<div id="training" data-section="training">
<FeatureCardTwentyFive
title="Strength Training"
description="Build muscle and increase strength with guided workouts."
tag="Training"
tagIcon={Dumbbell}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
features={[
{
title: 'Muscle Building',
description: 'Progressive programs to build and tone muscles.',
icon: Dumbbell,
mediaItems: [
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Weight lifting' },
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Muscle gain' }
]
},
{
title: 'Form Guide',
description: 'Video tutorials and form checks for exercises.',
icon: Zap,
mediaItems: [
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Exercise form' },
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Video guide' }
]
},
{
title: 'Recovery Tips',
description: 'Expert advice on rest days and recovery techniques.',
icon: Apple,
mediaItems: [
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Recovery' },
{ type: 'image', src: '/placeholders/placeholder1.webp', alt: 'Rest day' }
]
}
]}
buttons={[
{ text: 'View Training Plans', href: '#plans' }
]}
buttonAnimation="slide-up"
/>
</div>
<div id="training" data-section="training">
<FeatureCardTwentyFive
title="Core de Treinamento com IA"
description="Algoritmo adaptativo que recomenda treinos baseado em biometria. Escolha entre Casa (calistenia) ou Ginásio (máquinas/pesos) e isole por grupo muscular com modelo anatômico 3D interativo."
tag="Recomendação de IA"
tagIcon={Brain}
features={[
{
title: "Dualidade de Ambientes", description: "Treinos em Casa (peso corporal, calistenia) ou Ginásio (máquinas, pesos livres). Sistema diferencia automaticamente baseado em sua escolha e disponibilidade de equipamento.", icon: Dumbbell,
mediaItems: [
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/interactive-anatomical-body-model-showin-1773256980448-3cccd7b3.png?_wi=1", imageAlt: "Seleção de exercícios"
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/workout-execution-interface-showing-set--1773256980664-da11c464.png?_wi=1", imageAlt: "Execução de treino"
}
]
},
{
title: "Isolamento por Grupo Muscular", description: "Modelo anatômico 3D/2D interativo. Clique em um músculo (peitoral, glúteo, etc.) e veja lista filtrada de exercícios com foco nesse músculo específico.", icon: Zap,
mediaItems: [
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/interactive-anatomical-body-model-showin-1773256980448-3cccd7b3.png?_wi=2", imageAlt: "Anatomia interativa"
},
{
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/workout-execution-interface-showing-set--1773256980664-da11c464.png?_wi=2", imageAlt: "Exercícios filtrados"
}
]
}
]}
animationType="scale-rotate"
textboxLayout="default"
useInvertedBackground={false}
/>
</div>
<div id="workout-mode" data-section="workout-mode">
<ProductCardOne
title="Workout Modes"
description="Choose from various workout modes to suit your fitness level."
tag="Workouts"
tagIcon={Zap}
tagAnimation="slide-up"
textboxLayout="default"
gridVariant="four-items-2x2-equal-grid"
animationType="slide-up"
useInvertedBackground={false}
products={[
{
id: '1',
name: 'Beginner Workouts',
price: 'Free',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Beginner workout'
},
{
id: '2',
name: 'Intermediate Training',
price: '$9.99/mo',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Intermediate workout'
},
{
id: '3',
name: 'Advanced Programs',
price: '$19.99/mo',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Advanced workout'
},
{
id: '4',
name: 'Expert Coaching',
price: '$49.99/mo',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Expert coaching'
}
]}
buttons={[
{ text: 'View All Modes', href: '#modes' }
]}
buttonAnimation="slide-up"
/>
</div>
<div id="workout-mode" data-section="workout-mode">
<ProductCardOne
title="Modo 'Em Execução' - Tracking de Série"
description="Interface imersiva que transforma o treino em experiência interativa com cronômetro de descanso, registro de progresso e gráficos de progressão."
tag="Focus Mode"
tagIcon={Target}
products={[
{
id: "1", name: "Iniciar Série", price: "Botão Proeminente", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/workout-execution-interface-showing-set--1773256980664-da11c464.png?_wi=3", imageAlt: "Tela de série"
},
{
id: "2", name: "Cronômetro de Descanso", price: "Automático", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/workout-execution-interface-showing-set--1773256980664-da11c464.png?_wi=4", imageAlt: "Descanso entre séries"
},
{
id: "3", name: "Registrar Progresso", price: "Carga + Reps", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/performance-metrics-showcase-displaying--1773256982260-f9a5cff0.png?_wi=4", imageAlt: "Progresso salvo"
}
]}
animationType="slide-up"
textboxLayout="default"
useInvertedBackground={false}
gridVariant="three-columns-all-equal-width"
/>
</div>
<div id="nutrition" data-section="nutrition">
<PricingCardOne
title="Nutrition Plans"
description="Personalized meal plans tailored to your fitness goals."
tag="Nutrition"
tagIcon={Apple}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
plans={[
{
id: 'basic',
badge: 'Basic',
price: '$9.99',
subtitle: 'per month',
features: ['Basic meal plans', 'Calorie tracking', 'Recipe suggestions']
},
{
id: 'pro',
badge: 'Pro',
badgeIcon: Zap,
price: '$29.99',
subtitle: 'per month',
features: ['Advanced meal plans', 'Macro tracking', 'Expert guidance', 'Weekly recipes']
},
{
id: 'elite',
badge: 'Elite',
price: '$59.99',
subtitle: 'per month',
features: ['Custom meal plans', 'Personal nutritionist', '24/7 support', 'Supplement guide']
}
]}
buttons={[
{ text: 'Choose Plan', href: '/pricing' }
]}
buttonAnimation="slide-up"
/>
</div>
<div id="nutrition" data-section="nutrition">
<PricingCardOne
title="Nutrição Inteligente & Receituário"
description="Planos alimentares 100% sincronizados com suas metas de peso. Se quer perder peso, déficit calórico. Se quer ganhar massa, superávit inteligente."
tag="Sincronizado com Timeline"
tagIcon={Apple}
plans={[
{
id: "deficit", badge: "Perda de Peso", badgeIcon: TrendingDown,
price: "Déficit Calórico", subtitle: "Receitas otimizadas para queima de calorias", features: [
"Macros calculados automaticamente", "Prep time entre 15-30 min", "Proteína alta, carboidrato estratégico", "Rastreamento integrado", "Sugestões diárias personalizadas"
]
},
{
id: "surplus", badge: "Ganho de Massa", badgeIcon: TrendingUp,
price: "Superávit Estratégico", subtitle: "Nutrição para crescimento muscular", features: [
"Calorias calculadas para ganho", "Proteína máxima (2g por kg)", "Carbos pré e pós-treino", "Tempo de preparo eficiente", "Sincronizado com treino de força"
]
}
]}
animationType="slide-up"
textboxLayout="default"
useInvertedBackground={false}
/>
</div>
<div id="metrics" data-section="metrics">
<MetricCardFourteen
title="Your Progress"
tag="Metrics"
tagAnimation="slide-up"
metrics={[
{ id: '1', value: '2,450', description: 'Calories Burned' },
{ id: '2', value: '45', description: 'Workouts Completed' },
{ id: '3', value: '12', description: 'Pounds Lost' },
{ id: '4', value: '92%', description: 'Goal Achievement' }
]}
metricsAnimation="slide-up"
useInvertedBackground={false}
/>
</div>
<div id="metrics" data-section="metrics">
<MetricCardFourteen
title="Suas Conquistas Importam. Veja Cada Número."
tag="Performance Metrics"
tagAnimation="slide-up"
metrics={[
{
id: "1", value: "10.000+", description: "Passos diários rastreados em tempo real com motivação visual de progresso."
},
{
id: "2", value: "500 kg", description: "Volume total de peso levantado monitorado com progressão semanal automática."
},
{
id: "3", value: "150+ km", description: "Distância corrida mapeada com GPS, ritmo calculado e calorias precisas."
},
{
id: "4", value: "42 dias", description: "Sequência de treinos consistentes com badges de dedicação desbloqueados."
}
]}
metricsAnimation="slide-up"
useInvertedBackground={false}
/>
</div>
<div id="team" data-section="team">
<TeamCardOne
title="Meet Our Fitness Experts"
description="Learn from certified trainers and nutritionists."
tag="Team"
tagIcon={Zap}
tagAnimation="slide-up"
textboxLayout="default"
gridVariant="four-items-2x2-equal-grid"
animationType="slide-up"
useInvertedBackground={false}
members={[
{
id: '1',
name: 'Sarah Johnson',
role: 'Certified Personal Trainer',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Sarah Johnson'
},
{
id: '2',
name: 'Mike Chen',
role: 'Nutrition Specialist',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Mike Chen'
},
{
id: '3',
name: 'Emma Davis',
role: 'Yoga Instructor',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Emma Davis'
},
{
id: '4',
name: 'James Wilson',
role: 'Strength Coach',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'James Wilson'
}
]}
buttons={[
{ text: 'Meet the Team', href: '#experts' }
]}
buttonAnimation="slide-up"
/>
</div>
<div id="team" data-section="team">
<TeamCardOne
title="Comunidade de Atletas Profissionais"
description="Feed social onde você compartilha rotas de corrida, estatísticas de treino e compete em rankings semanais com outros usuários."
tag="Gamificação Social"
tagIcon={Users}
members={[
{
id: "1", name: "Marcus A.", role: "Elite Runner", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/professional-athlete-portrait-male-fitne-1773256979726-5009f852.png?_wi=1", imageAlt: "Marcus A."
},
{
id: "2", name: "Ana L.", role: "Strength Coach", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/fit-female-athlete-portrait-determined-e-1773256980310-c05dce2f.png?_wi=1", imageAlt: "Ana L."
},
{
id: "3", name: "Rafael S.", role: "Nutrition Expert", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/athletic-male-trainer-portrait-confident-1773256979906-c5e05a88.png?_wi=1", imageAlt: "Rafael S."
},
{
id: "4", name: "Sofia M.", role: "Fitness Trainer", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/female-fitness-coach-portrait-profession-1773256979710-97e8b5fe.png?_wi=1", imageAlt: "Sofia M."
}
]}
animationType="blur-reveal"
textboxLayout="default"
useInvertedBackground={false}
gridVariant="four-items-2x2-equal-grid"
/>
</div>
<div id="testimonials" data-section="testimonials">
<TestimonialCardTwelve
testimonials={[
{
id: '1',
name: 'Alex Rodriguez',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Alex Rodriguez'
},
{
id: '2',
name: 'Lisa Chen',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Lisa Chen'
},
{
id: '3',
name: 'Michael Brown',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Michael Brown'
},
{
id: '4',
name: 'Jessica Taylor',
imageSrc: '/placeholders/placeholder1.webp',
imageAlt: 'Jessica Taylor'
}
]}
cardTitle="Trusted by 500k+ users worldwide"
cardTag="Success Stories"
cardTagIcon={Zap}
cardAnimation="slide-up"
useInvertedBackground={false}
/>
</div>
<div id="testimonials" data-section="testimonials">
<TestimonialCardTwelve
testimonials={[
{
id: "1", name: "Carlos M.", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/professional-athlete-portrait-male-fitne-1773256979726-5009f852.png?_wi=2", imageAlt: "Carlos M."
},
{
id: "2", name: "Beatriz R.", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/fit-female-athlete-portrait-determined-e-1773256980310-c05dce2f.png?_wi=2", imageAlt: "Beatriz R."
},
{
id: "3", name: "Diego P.", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/athletic-male-trainer-portrait-confident-1773256979906-c5e05a88.png?_wi=2", imageAlt: "Diego P."
},
{
id: "4", name: "Juliana T.", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/female-fitness-coach-portrait-profession-1773256979710-97e8b5fe.png?_wi=2", imageAlt: "Juliana T."
},
{
id: "5", name: "Lucas F.", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/young-athlete-male-portrait-energetic-ex-1773256982698-63e4e494.png", imageAlt: "Lucas F."
},
{
id: "6", name: "Marina S.", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoRNSPr0mCBj85JKsHl7qxTHsl/female-athlete-portrait-fit-build-profes-1773256980134-0faaa8fa.png", imageAlt: "Marina S."
}
]}
cardTitle="Mais de 150 mil atletas já transformaram seu corpo com FitFlow Pro"
cardTag="Veja o que eles dizem"
cardTagIcon={Star}
cardAnimation="blur-reveal"
useInvertedBackground={false}
/>
</div>
<div id="social-proof" data-section="social-proof">
<SocialProofOne
title="Featured In"
description="Recognized by leading fitness and wellness publications."
tag="Social Proof"
tagIcon={Zap}
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={false}
names={['Fitness Magazine', 'Wellness Today', 'Health News', 'Trainer Weekly', 'Sports Life']}
buttons={[
{ text: 'Read Our Press', href: '#press' }
]}
buttonAnimation="slide-up"
/>
</div>
<div id="social-proof" data-section="social-proof">
<SocialProofOne
title="Confiado pelos Maiores Aplicativos e Plataformas de Fitness"
description="FitFlow Pro integra-se perfeitamente com os ecossistemas de saúde mais populares do mundo."
tag="Parcerias Premium"
tagIcon={Zap}
names={[
"Apple Fitness", "Strava", "Fitbit", "Google Fit", "Peloton", "MyFitnessPal", "Garmin", "Nike Training"
]}
logos={[
"http://img.b2bpic.net/free-vector/heart-love-logo_126523-2763.jpg", "http://img.b2bpic.net/free-photo/woman-with-phone-sportswear_1303-8805.jpg", "http://img.b2bpic.net/free-vector/flat-design-fitness-trackers-heart-rate-menu_23-2148515781.jpg", "http://img.b2bpic.net/free-photo/close-up-woman-holding-phone_23-2148889655.jpg", "http://img.b2bpic.net/free-photo/close-up-man-using-phone-while-electric-bike_23-2149098678.jpg", "http://img.b2bpic.net/free-vector/fitness-trackers-concept_23-2148527033.jpg", "http://img.b2bpic.net/free-photo/cropped-view-sexy-bodybuilder-showing-thumbs-up_1153-6281.jpg", "http://img.b2bpic.net/free-photo/phone-muscular-hand-guy-sitting-city-morning-he-holds-bottle-water-headphones_197531-1155.jpg"
]}
textboxLayout="default"
useInvertedBackground={false}
speed={40}
showCard={true}
/>
</div>
<div id="contact" data-section="contact">
<ContactText
text="Ready to transform your fitness journey? Start your free trial today and unlock your potential."
animationType="entrance-slide"
background={{ variant: 'sparkles-gradient' }}
useInvertedBackground={false}
buttons={[
{ text: 'Start Free Trial', href: '/signup' },
{ text: 'Contact Us', href: 'mailto:info@fitflow.com' }
]}
/>
</div>
<div id="contact" data-section="contact">
<ContactText
text="Pronto para transformar seu corpo? Começar seu teste gratuito de 30 dias agora e veja por que 150 mil atletas confiam em FitFlow Pro."
animationType="entrance-slide"
buttons={[
{ text: "Começar Teste Grátis", href: "contact" },
{ text: "Conversar com Especialista", href: "#contact" }
]}
background={{ variant: "sparkles-gradient" }}
useInvertedBackground={false}
/>
</div>
<div id="footer" data-section="footer">
<FooterLogoEmphasis
logoText="FitFlow"
columns={[
{
items: [
{ label: 'About', href: '#about' },
{ label: 'Blog', href: '#blog' },
{ label: 'Careers', href: '#careers' }
]
},
{
items: [
{ label: 'Privacy', href: '#privacy' },
{ label: 'Terms', href: '#terms' },
{ label: 'Contact', href: '#contact' }
]
},
{
items: [
{ label: 'Twitter', href: 'https://twitter.com' },
{ label: 'Instagram', href: 'https://instagram.com' },
{ label: 'LinkedIn', href: 'https://linkedin.com' }
]
}
]}
/>
</div>
</ThemeProvider>
<div id="footer" data-section="footer">
<FooterLogoEmphasis
columns={[
{
items: [
{ label: "Dashboard", href: "#dashboard" },
{ label: "Treino", href: "#training" },
{ label: "Nutrição", href: "#nutrition" }
]
},
{
items: [
{ label: "Cardio Hub", href: "#cardio" },
{ label: "Comunidade", href: "#community" },
{ label: "Perfil", href: "#profile" }
]
},
{
items: [
{ label: "Blog", href: "#blog" },
{ label: "Ajuda", href: "#help" },
{ label: "Suporte", href: "#support" }
]
},
{
items: [
{ label: "Privacidade", href: "#privacy" },
{ label: "Termos", href: "#terms" },
{ label: "Contato", href: "#contact" }
]
}
]}
logoText="FitFlow Pro"
/>
</div>
</ThemeProvider>
</WorkoutDataIntegration>
);
};
export default Page;
}

View File

@@ -2,107 +2,91 @@
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
import { useState } from 'react';
import { Mail, Lock, User, Eye, EyeOff, ArrowRight, CheckCircle } from 'lucide-react';
import { useState } from "react";
import { Eye, EyeOff, Mail, Lock, User, CheckCircle2, AlertCircle } from 'lucide-react';
import Input from '@/components/form/Input';
export default function SignupPage() {
const [formData, setFormData] = useState({
fullName: '',
email: '',
password: '',
confirmPassword: ''
name: "", email: "", password: "", confirmPassword: ""
});
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [errors, setErrors] = useState<Record<string, string>>({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [passwordStrength, setPasswordStrength] = useState(0);
const [agreedToTerms, setAgreedToTerms] = useState(false);
const [errors, setErrors] = useState<{ [key: string]: string }>({});
const [isSubmitted, setIsSubmitted] = useState(false);
const [passwordStrength, setPasswordStrength] = useState<"weak" | "medium" | "strong" | "">("");
const calculatePasswordStrength = (pwd: string) => {
let strength = 0;
if (pwd.length >= 8) strength++;
if (pwd.match(/[a-z]/) && pwd.match(/[A-Z]/)) strength++;
if (pwd.match(/[0-9]/)) strength++;
if (pwd.match(/[^a-zA-Z0-9]/)) strength++;
setPasswordStrength(strength);
const calculatePasswordStrength = (pwd: string): "weak" | "medium" | "strong" | "" => {
if (!pwd) return "";
if (pwd.length < 8) return "weak";
if (/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{8}$/.test(pwd)) return "strong";
return "medium";
};
const handleInputChange = (field: string, value: string) => {
setFormData(prev => ({
...prev,
[field]: value
}));
if (field === "password") {
setPasswordStrength(calculatePasswordStrength(value));
}
};
const validateForm = () => {
const newErrors: Record<string, string> = {};
const newErrors: { [key: string]: string } = {};
if (!formData.fullName.trim()) {
newErrors.fullName = 'Full name is required';
if (!formData.name.trim()) {
newErrors.name = "Nome é obrigatório";
} else if (formData.name.trim().length < 2) {
newErrors.name = "Nome deve ter no mínimo 2 caracteres";
}
if (!formData.email) {
newErrors.email = 'Email is required';
newErrors.email = "Email é obrigatório";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
newErrors.email = 'Please enter a valid email';
newErrors.email = "Email inválido";
}
if (!formData.password) {
newErrors.password = 'Password is required';
newErrors.password = "Senha é obrigatória";
} else if (formData.password.length < 8) {
newErrors.password = 'Password must be at least 8 characters';
} else if (passwordStrength < 2) {
newErrors.password = 'Password is too weak';
newErrors.password = "Senha deve ter no mínimo 8 caracteres";
}
if (!formData.confirmPassword) {
newErrors.confirmPassword = 'Please confirm your password';
} else if (formData.password !== formData.confirmPassword) {
newErrors.confirmPassword = 'Passwords do not match';
if (formData.password !== formData.confirmPassword) {
newErrors.confirmPassword = "As senhas não correspondem";
}
if (!agreedToTerms) {
newErrors.terms = 'You must agree to the terms';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
return newErrors;
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
if (name === 'password') {
calculatePasswordStrength(value);
}
};
const handleSubmit = async (e: React.FormEvent) => {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const newErrors = validateForm();
setErrors(newErrors);
if (!validateForm()) {
return;
}
setIsSubmitting(true);
try {
// Simulated API call
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Signup attempt:', { ...formData, confirmPassword: undefined });
// In a real app, you would create account and redirect
} finally {
setIsSubmitting(false);
if (Object.keys(newErrors).length === 0) {
setIsSubmitted(true);
console.log("Signup attempt:", formData);
setTimeout(() => {
setIsSubmitted(false);
}, 2000);
}
};
const getPasswordStrengthColor = () => {
if (passwordStrength <= 1) return '#ef4444';
if (passwordStrength <= 2) return '#eab308';
if (passwordStrength <= 3) return '#f97316';
return '#22c55e';
};
const getPasswordStrengthText = () => {
if (!formData.password) return '';
if (passwordStrength <= 1) return 'Weak';
if (passwordStrength <= 2) return 'Fair';
if (passwordStrength <= 3) return 'Good';
return 'Strong';
const getStrengthColor = () => {
switch (passwordStrength) {
case "weak":
return "text-red-500";
case "medium":
return "text-yellow-500";
case "strong":
return "text-green-500";
default:
return "text-foreground/30";
}
};
return (
@@ -122,283 +106,168 @@ export default function SignupPage() {
<NavbarStyleCentered
navItems={[
{ name: "Dashboard", id: "/" },
{ name: "Treino", id: "#training" },
{ name: "Nutrição", id: "#nutrition" },
{ name: "Comunidade", id: "#community" },
{ name: "Perfil", id: "#profile" }
{ name: "Treino", id: "training" },
{ name: "Nutrição", id: "nutrition" },
{ name: "Comunidade", id: "community" },
{ name: "Perfil", id: "profile" }
]}
button={{ text: "Entrar", href: "/login" }}
brandName="FitFlow Pro"
/>
</div>
<div id="signup" data-section="signup" className="min-h-screen flex items-center justify-center px-4 py-20">
<div className="min-h-[calc(100vh-80px)] flex items-center justify-center py-12 px-4">
<div className="w-full max-w-md">
<div className="rounded-2xl border border-opacity-20 p-8 backdrop-blur-sm" style={{
backgroundColor: 'var(--color-card)',
borderColor: 'var(--color-foreground)'
}}>
{/* Header */}
<div className="mb-8 text-center">
<h1 className="text-3xl font-bold mb-2" style={{ color: 'var(--color-foreground)' }}>
Create Account
</h1>
<p className="text-sm" style={{ color: 'var(--color-foreground)', opacity: 0.7 }}>
Join 150k+ athletes transforming their bodies
</p>
<div className="bg-card rounded-3xl shadow-lg p-8 border border-accent/10">
<div className="mb-8">
<h1 className="text-3xl font-extrabold text-foreground mb-2">Começar Agora</h1>
<p className="text-foreground/70">Crie sua conta e inicie sua transformação</p>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="space-y-5">
{/* Full Name Field */}
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="fullName" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Full Name
<label className="block text-sm font-medium text-foreground mb-2">
<div className="flex items-center gap-2">
<User size={16} />
Nome Completo
</div>
</label>
<div className="relative">
<User className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="fullName"
type="text"
name="fullName"
value={formData.fullName}
onChange={handleChange}
placeholder="Your name"
className="w-full pl-10 pr-4 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.fullName ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
/>
</div>
{errors.fullName && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
{errors.fullName}
</p>
<Input
value={formData.name}
onChange={(value) => handleInputChange("name", value)}
type="text"
placeholder="Seu nome"
required
className={errors.name ? "border-red-500" : ""}
/>
{errors.name && (
<div className="flex items-center gap-1 text-red-500 text-sm mt-1">
<AlertCircle size={14} />
{errors.name}
</div>
)}
</div>
{/* Email Field */}
<div>
<label htmlFor="email" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Email Address
<label className="block text-sm font-medium text-foreground mb-2">
<div className="flex items-center gap-2">
<Mail size={16} />
Email
</div>
</label>
<div className="relative">
<Mail className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="email"
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="you@example.com"
className="w-full pl-10 pr-4 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.email ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
/>
</div>
<Input
value={formData.email}
onChange={(value) => handleInputChange("email", value)}
type="email"
placeholder="seu@email.com"
required
className={errors.email ? "border-red-500" : ""}
/>
{errors.email && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
<div className="flex items-center gap-1 text-red-500 text-sm mt-1">
<AlertCircle size={14} />
{errors.email}
</p>
</div>
)}
</div>
{/* Password Field */}
<div>
<label htmlFor="password" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Password
<label className="block text-sm font-medium text-foreground mb-2">
<div className="flex items-center gap-2">
<Lock size={16} />
Senha
</div>
</label>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="password"
type={showPassword ? 'text' : 'password'}
name="password"
<Input
value={formData.password}
onChange={handleChange}
onChange={(value) => handleInputChange("password", value)}
type={showPassword ? "text" : "password"}
placeholder="••••••••"
className="w-full pl-10 pr-10 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.password ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
required
className={errors.password ? "border-red-500" : "pr-10"}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-3.5 transition-opacity hover:opacity-75"
className="absolute right-3 top-1/2 -translate-y-1/2 text-foreground/50 hover:text-foreground transition-colors"
aria-label={showPassword ? "Ocultar senha" : "Mostrar senha"}
>
{showPassword ? (
<EyeOff className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
) : (
<Eye className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
)}
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
{/* Password Strength Indicator */}
{formData.password && (
<div className="mt-2 flex items-center gap-2">
<div className="flex-1 h-1.5 rounded-full bg-gray-300" style={{ background: 'var(--color-background)' }}>
<div
className="h-full rounded-full transition-all"
style={{
width: `${(passwordStrength / 4) * 100}%`,
backgroundColor: getPasswordStrengthColor(),
}}
/>
</div>
<span className="text-xs font-medium" style={{ color: getPasswordStrengthColor() }}>
{getPasswordStrengthText()}
</span>
{passwordStrength && (
<div className={`flex items-center gap-1 text-xs mt-2 ${getStrengthColor()}`}>
<CheckCircle2 size={12} />
Força: {passwordStrength === "weak" ? "Fraca" : passwordStrength === "medium" ? "Média" : "Forte"}
</div>
)}
{errors.password && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
<div className="flex items-center gap-1 text-red-500 text-sm mt-1">
<AlertCircle size={14} />
{errors.password}
</p>
</div>
)}
</div>
{/* Confirm Password Field */}
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Confirm Password
<label className="block text-sm font-medium text-foreground mb-2">
<div className="flex items-center gap-2">
<Lock size={16} />
Confirmar Senha
</div>
</label>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="confirmPassword"
type={showConfirmPassword ? 'text' : 'password'}
name="confirmPassword"
<Input
value={formData.confirmPassword}
onChange={handleChange}
onChange={(value) => handleInputChange("confirmPassword", value)}
type={showConfirmPassword ? "text" : "password"}
placeholder="••••••••"
className="w-full pl-10 pr-10 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.confirmPassword ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
required
className={errors.confirmPassword ? "border-red-500" : "pr-10"}
/>
<button
type="button"
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
className="absolute right-3 top-3.5 transition-opacity hover:opacity-75"
className="absolute right-3 top-1/2 -translate-y-1/2 text-foreground/50 hover:text-foreground transition-colors"
aria-label={showConfirmPassword ? "Ocultar senha" : "Mostrar senha"}
>
{showConfirmPassword ? (
<EyeOff className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
) : (
<Eye className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
)}
{showConfirmPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
{errors.confirmPassword && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
<div className="flex items-center gap-1 text-red-500 text-sm mt-1">
<AlertCircle size={14} />
{errors.confirmPassword}
</p>
)}
</div>
{/* Terms Agreement */}
<div className="pt-2">
<label className="flex items-start gap-3 cursor-pointer">
<div className="mt-0.5">
{agreedToTerms ? (
<CheckCircle className="w-5 h-5" style={{ color: 'var(--color-primary-cta)' }} />
) : (
<div className="w-5 h-5 rounded border" style={{ borderColor: 'var(--color-foreground)', opacity: 0.3 }} />
)}
</div>
<span className="text-sm" style={{ color: 'var(--color-foreground)', opacity: 0.7 }}>
I agree to the{' '}
<a href="#terms" className="font-semibold hover:underline" style={{ color: 'var(--color-primary-cta)' }}>
Terms of Service
</a>
{' '}and{' '}
<a href="#privacy" className="font-semibold hover:underline" style={{ color: 'var(--color-primary-cta)' }}>
Privacy Policy
</a>
</span>
<input
type="checkbox"
checked={agreedToTerms}
onChange={(e) => setAgreedToTerms(e.target.checked)}
className="hidden"
/>
</label>
{errors.terms && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
{errors.terms}
</p>
)}
</div>
{/* Submit Button */}
<button
type="submit"
disabled={isSubmitting}
className="w-full py-2.5 rounded-lg font-semibold transition-all flex items-center justify-center gap-2 disabled:opacity-50 mt-6"
style={{
backgroundColor: 'var(--color-primary-cta)',
color: 'white',
}}
disabled={isSubmitted}
className="w-full py-3 px-4 bg-primary-cta hover:opacity-90 disabled:opacity-50 text-white font-semibold rounded-full transition-all duration-300 transform hover:scale-105"
>
{isSubmitting ? 'Creating Account...' : 'Create Account'}
{!isSubmitting && <ArrowRight className="w-4 h-4" />}
{isSubmitted ? "Criando conta..." : "Criar Conta"}
</button>
</form>
{/* Divider */}
<div className="my-6 flex items-center gap-4">
<div className="flex-1 h-px" style={{ backgroundColor: 'var(--color-foreground)', opacity: 0.1 }} />
<span className="text-xs" style={{ color: 'var(--color-foreground)', opacity: 0.5 }}>OR</span>
<div className="flex-1 h-px" style={{ backgroundColor: 'var(--color-foreground)', opacity: 0.1 }} />
<div className="mt-6 text-center text-sm text-foreground/70">
<p>
tem conta?{" "}
<a href="/login" className="text-primary-cta font-semibold hover:underline">
Fazer login
</a>
</p>
</div>
{/* Social Signup */}
<div className="grid grid-cols-2 gap-3">
<button
type="button"
className="py-2.5 rounded-lg border font-medium transition-all hover:opacity-80"
style={{
backgroundColor: 'var(--color-background)',
borderColor: 'var(--color-foreground)',
color: 'var(--color-foreground)',
}}
>
Google
</button>
<button
type="button"
className="py-2.5 rounded-lg border font-medium transition-all hover:opacity-80"
style={{
backgroundColor: 'var(--color-background)',
borderColor: 'var(--color-foreground)',
color: 'var(--color-foreground)',
}}
>
Apple
</button>
<div className="mt-6 pt-6 border-t border-accent/10 text-center text-xs text-foreground/50">
<p>Teste gratuito por 30 dias. Sem cartão de crédito necessário.</p>
<p className="mt-2">Ao criar uma conta, você concorda com nossos Termos de Serviço e Política de Privacidade.</p>
</div>
{/* Login Link */}
<p className="mt-6 text-center text-sm" style={{ color: 'var(--color-foreground)', opacity: 0.7 }}>
Already have an account?{' '}
<a href="/login" className="font-semibold hover:underline" style={{ color: 'var(--color-primary-cta)' }}>
Sign in
</a>
</p>
</div>
</div>
</div>
</ThemeProvider>
);
}
}