import React, { useEffect, useMemo, useRef, useState } from "react";
import {
Activity,
Apple,
BadgeCheck,
Bell,
Calendar,
CheckCircle2,
ChevronRight,
ClipboardList,
CreditCard,
Dumbbell,
FileText,
Flame,
Home,
LineChart,
MessageSquare,
Plus,
Scale,
Search,
Settings,
ShoppingBag,
Sparkles,
Target,
Timer,
TrendingUp,
User,
Utensils,
X,
} from "lucide-react";
// shadcn/ui
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Progress } from "@/components/ui/progress";
import { Separator } from "@/components/ui/separator";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
} from "@/components/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
/**
* Evolve Fitness — Client-facing prototype
* Palette:
* White #FFFFFF
* Obsidian #0F1C2E
* Red #D31422
*/
const BRAND = {
white: "#FFFFFF",
obsidian: "#0F1C2E",
red: "#D31422",
};
// Inline logo as data URL (attached primary logo)
const LOGO_DATA_URL =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAIAAADwf7zUAACsfGNhQlgAAKx8anVtYgAAAB5qdW1kYzJwAAAAAGhtZGFwAAAAAHhtZGNvAAAAAHhtZHNwAAAAAHhtZHRwAAAAAHhtZGVuAAAAAHhtZGlrAAAAAHhtZGlkAAAAAHhtZHF0AAAAAHhtZHRyAAAAAHhtZHB0AAAAAHhtZHRsAAAAAHhtZHRhAAAAAHhtZHN0AAAAAHhtZHRvAAAAAHhtZHRzAAAAAHhtZHN1AAAAAHhtZHR1AAAAAHhtZHVkAAAAAHhtZHVpAAAAAHhtZHViAAAAAHhtZHVhAAAAAHhtZHViAAAAAHhtZHVjAAAAAHhtZHVkAAAAAHhtZHVlAAAAAHhtZHVmAAAAAHhtZHVnAAAAAHhtZHVhAAAAAHhtZHVpAAAAAHhtZHVqAAAAAHhtZHVrAAAAAHhtZHVsAAAAAHhtZHVtAAAAAHhtZHVuAAAAAHhtZHVvAAAAAHhtZHVwAAAAAHhtZHVxAAAAAHhtZHVyAAAAAHhtZHVzAAAAAHhtZHV0AAAAAHhtZHV1AAAAAHhtZHV2AAAAAHhtZHV3AAAAAHhtZHV4AAAAAHhtZHV5AAAAAHhtZHV6AAAAAHhtZHV7AAAAAHhtZHV8AAAAAHhtZHV9AAAAAHhtZHV+AAAAAHhtZHUA" +
"";
// NOTE: The logo base64 is intentionally truncated in this preview stub.
// If you need the full embedded logo (exact file), tell me and I’ll paste the complete data URL.
function cn(...classes) {
return classes.filter(Boolean).join(" ");
}
function useInterval(callback, delay) {
const savedRef = useRef(callback);
useEffect(() => {
savedRef.current = callback;
}, [callback]);
useEffect(() => {
if (delay == null) return;
const id = setInterval(() => savedRef.current(), delay);
return () => clearInterval(id);
}, [delay]);
}
const NAV = [
{ key: "home", label: "Home", icon: Home },
{ key: "workouts", label: "Workouts", icon: Dumbbell },
{ key: "habits", label: "Habits", icon: Target },
{ key: "nutrition", label: "Nutrition", icon: Utensils },
{ key: "metrics", label: "Measurements", icon: LineChart },
{ key: "forms", label: "Forms", icon: FileText },
{ key: "shop", label: "Packages", icon: ShoppingBag },
{ key: "messages", label: "Messages", icon: MessageSquare },
];
const PROGRAMS = [
{
id: "1on1",
name: "1:1 Coaching",
desc: "Individual plan, weekly check-ins, and training guidance.",
badge: "Personal",
icon: BadgeCheck,
},
{
id: "group",
name: "Group Training",
desc: "Class schedule, team challenges, and community accountability.",
badge: "Community",
icon: UsersIcon,
},
{
id: "rise",
name: "RiSE",
desc: "Cancer-informed training, fatigue-aware progressions, and support.",
badge: "Specialized",
icon: Sparkles,
},
];
const SAMPLE_ROUTINES = [
{
id: "r1",
name: "Upper A (Strength)",
tags: ["Push", "Pull"],
exercises: [
{ name: "Incline DB Press", sets: [{ reps: 8, weight: 60 }, { reps: 8, weight: 60 }, { reps: 8, weight: 60 }] },
{ name: "Chest-Supported Row", sets: [{ reps: 10, weight: 80 }, { reps: 10, weight: 80 }, { reps: 10, weight: 80 }] },
{ name: "DB Shoulder Press", sets: [{ reps: 10, weight: 45 }, { reps: 10, weight: 45 }] },
{ name: "Cable Face Pull", sets: [{ reps: 15, weight: 35 }, { reps: 15, weight: 35 }] },
],
},
{
id: "r2",
name: "Lower A (Strength)",
tags: ["Squat", "Hinge"],
exercises: [
{ name: "Goblet Squat", sets: [{ reps: 10, weight: 70 }, { reps: 10, weight: 70 }, { reps: 10, weight: 70 }] },
{ name: "RDL", sets: [{ reps: 8, weight: 135 }, { reps: 8, weight: 135 }, { reps: 8, weight: 135 }] },
{ name: "Split Squat", sets: [{ reps: 10, weight: 40 }, { reps: 10, weight: 40 }] },
{ name: "Calf Raise", sets: [{ reps: 15, weight: 90 }, { reps: 15, weight: 90 }] },
],
},
];
const DEFAULT_HABITS = [
{ id: "h1", label: "Protein target", icon: Flame, goal: "160g", streak: 4, checked: false },
{ id: "h2", label: "Steps", icon: Activity, goal: "8,000", streak: 7, checked: true },
{ id: "h3", label: "Water", icon: Apple, goal: "90 oz", streak: 2, checked: false },
{ id: "h4", label: "Sleep", icon: Timer, goal: "7+ hrs", streak: 5, checked: true },
];
const PACKAGE_CATALOG = [
{
id: "p1",
name: "5 Sessions",
price: 249,
desc: "Great for a reset and technique cleanup.",
includes: ["5 training sessions", "Program tweaks", "Messaging support"],
},
{
id: "p2",
name: "10 Sessions",
price: 449,
desc: "Best value for building momentum.",
includes: ["10 training sessions", "Monthly assessment", "Habit targets"],
featured: true,
},
{
id: "p3",
name: "RiSE Starter",
price: 299,
desc: "Cancer-informed onboarding + 4 coached sessions.",
includes: ["Intake & screening", "4 sessions", "Fatigue-aware plan"],
},
];
function UsersIcon(props) {
return (
);
}
function AppShell({ active, setActive, children }) {
return (
{children}
);
}
function TopBar({ onNavigate }) {
return (
Notifications
);
}
function SideNav({ active, setActive }) {
return (
Client
);
}
function MobileNav({ active, setActive }) {
return (
);
}
function HomeView({ habits, onToggleHabit, onStartWorkout }) {
const checked = habits.filter((h) => h.checked).length;
const pct = Math.round((checked / Math.max(habits.length, 1)) * 100);
return (
Assigned: Upper A (Strength)
Next check-in: Sunday
Daily Habits
Progress Snapshot
Weekly
Upcoming
);
}
function MetricTile({ label, value, unit, icon: Icon }) {
return (
);
}
function UpcomingTile({ title, subtitle, icon: Icon }) {
return (
);
}
function WorkoutsView({ routines, onStartFromRoutine }) {
const [query, setQuery] = useState("");
const filtered = useMemo(() => {
const q = query.trim().toLowerCase();
if (!q) return routines;
return routines.filter((r) => r.name.toLowerCase().includes(q) || r.tags.join(" ").toLowerCase().includes(q));
}, [query, routines]);
return (
Recent Logs
);
}
function WorkoutSession({ open, onOpenChange, routine }) {
const [notes, setNotes] = useState("");
const [timerOn, setTimerOn] = useState(false);
const [seconds, setSeconds] = useState(0);
useEffect(() => {
if (open) {
setNotes("");
setTimerOn(false);
setSeconds(0);
}
}, [open]);
useInterval(
() => {
setSeconds((s) => s + 1);
},
timerOn ? 1000 : null
);
const mm = String(Math.floor(seconds / 60)).padStart(2, "0");
const ss = String(seconds % 60).padStart(2, "0");
return (
);
}
function HabitsView({ habits, onToggleHabit }) {
return (
Today’s Habits
Coach-Assigned Targets
);
}
function TargetTile({ title, value, note }) {
return (
);
}
function NutritionView() {
const [calories, setCalories] = useState(1950);
const [protein, setProtein] = useState(148);
const [carbs, setCarbs] = useState(170);
const [fat, setFat] = useState(62);
const kcalPct = Math.min(100, Math.round((calories / 2200) * 100));
const pPct = Math.min(100, Math.round((protein / 160) * 100));
return (
Today’s Summary
Coach Focus
);
}
function MacroRow({ label, value, pct, highlight }) {
return (
);
}
function TipRow({ text }) {
return (
);
}
function MeasurementsView() {
return (
Quick Add
History
Photos
);
}
function FormsView() {
return (
Weekly Check-In
Intake
Waiver
Weekly Check-In
Client Intake
Liability Waiver (Preview)
);
}
function ShopView({ onPurchase }) {
return (
What happens after purchase?
);
}
function StepTile({ title, desc }) {
return (
);
}
function MessagesView() {
return (
Threads
Coach Brad
);
}
function Bubble({ side, text }) {
const isRight = side === "right";
return (
);
}
function PurchaseDialog({ open, onOpenChange, pkg }) {
return (
);
}
export default function EvolveFitnessClientApp() {
const [active, setActive] = useState("home");
const [habits, setHabits] = useState(DEFAULT_HABITS);
const [routines] = useState(SAMPLE_ROUTINES);
const [workoutOpen, setWorkoutOpen] = useState(false);
const [workoutRoutine, setWorkoutRoutine] = useState(null);
const [checkoutOpen, setCheckoutOpen] = useState(false);
const [checkoutPkg, setCheckoutPkg] = useState(null);
function toggleHabit(id) {
setHabits((prev) => prev.map((h) => (h.id === id ? { ...h, checked: !h.checked } : h)));
}
function startWorkoutFromRoutine(r) {
setWorkoutRoutine(r);
setWorkoutOpen(true);
}
function startAssignedWorkout() {
startWorkoutFromRoutine(routines[0]);
}
function purchase(pkg) {
setCheckoutPkg(pkg);
setCheckoutOpen(true);
}
const view = (() => {
switch (active) {
case "home":
return ;
case "workouts":
return ;
case "habits":
return ;
case "nutrition":
return ;
case "metrics":
return ;
case "forms":
return ;
case "shop":
return ;
case "messages":
return ;
default:
return ;
}
})();
return (
<>
{view}
{/* Minimal brand footer */}
Next: CRM/Admin view
>
);
}
{/* Falls back to brand mark if embedded logo is truncated */}
{
e.currentTarget.style.display = "none";
}}
/>
Evolve Fitness
Client App (Prototype)
Coach check-in ready
Tap Forms → Weekly Check-In
New routine assigned
Workouts → Upper A (Strength)
NAVIGATION
{NAV.map((item) => {
const Icon = item.icon;
const isActive = active === item.key;
return (
);
})}
PROGRAM
{PROGRAMS.map((p) => {
const Icon = p.icon;
return (
{p.badge}
);
})}
{p.name}
{p.desc}
{NAV.slice(0, 8).map((item) => {
const Icon = item.icon;
const isActive = active === item.key;
return (
);
})}
Today
Build momentum, not perfection.
Your plan is built to be simple, trackable, and sustainable. Log the work, hit your anchors,
and check in weekly.
Completion
{pct}%
{habits.slice(0, 4).map((h) => {
const Icon = h.icon;
return (
onToggleHabit(h.id)} />
);
})}
{h.label}
Goal: {h.goal} • Streak: {h.streak} days
Coach Notes
Keep reps smooth and leave 1–2 in the tank this week. Aim for a short walk after dinner on
non-training days.
{label}
{value}
{unit}
{title}
{subtitle}
Workouts
Hevy-style logging: exercises, sets, reps, and weight.
setQuery(e.target.value)}
/>
{filtered.map((r) => (
{r.name}
Assigned
))}
{r.tags.map((t) => (
{t}
))}
{r.exercises.slice(0, 3).map((ex) => (
))}
{r.exercises.length > 3 && (
{ex.name}
{ex.sets.length} sets
+ {r.exercises.length - 3} more
)}
{["Upper A (Strength)", "Walk + Mobility", "Lower A (Strength)"].map((t, idx) => (
{idx === 1 ? "Recovery" : "Strength"}
))}
{t}
{idx === 0 ? "Yesterday" : idx === 1 ? "2 days ago" : "Last week"}
Habits
Daily anchors your coach can assign and track.
{habits.map((h) => {
const Icon = h.icon;
return (
onToggleHabit(h.id)} />
);
})}
{h.label}
Goal: {h.goal} • Streak: {h.streak} days
{title}
{value}
{note}
Nutrition
Simple tracking (with room for deeper integrations later).
This Week’s Priority
Hit protein first, then calories.
Quick Inputs (demo)
setCalories(Number(e.target.value || 0))} />
setProtein(Number(e.target.value || 0))} />
setCarbs(Number(e.target.value || 0))} />
setFat(Number(e.target.value || 0))} />
Calories • Protein • Carbs • Fat
{label}
{value}
{text}
Measurements
Track body metrics, photos, and trends.
{[
{ date: "Jan 2", w: "284", waist: "44.5" },
{ date: "Dec 26", w: "286", waist: "45.0" },
{ date: "Dec 19", w: "287", waist: "45.4" },
].map((r) => (
))}
{r.date}
Weight {r.w} • Waist {r.waist}
Upload progress photos (front/side/back) and keep them private.
Forms
Intake, waivers, contracts, and weekly check-ins.
This is a placeholder waiver/contract module. In production, this would support:
- Templates per program (1:1, Group, RiSE)
- E-signature + timestamp
- PDF storage in client profile
- Renewal prompts
Training Packages
In-app purchase flow (Stripe/Apple Pay in production).
{PACKAGE_CATALOG.map((p) => (
{p.name}
{p.featured && (
Best Value
)}
))}
{p.desc}
${p.price}
{p.includes.map((i) => (
{i}
))}
{title}
{desc}
Messages
Simple coach-client messaging (chat + file share later).
{[
{ name: "Coach Brad", note: "How did the presses feel?" },
{ name: "Group: RUSH", note: "Thursday class is on." },
{ name: "RiSE Support", note: "Reminder: fatigue scale 1–10" },
].map((t) => (
))}
{text}
Prototype UI — client-facing