UI refactoring: Profile, History, and Plans Components
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Plus, Trash2, PlayCircle, Dumbbell, Save, X, ChevronRight, List, ArrowUp, ArrowDown, Scale, Edit2, User, Flame, Timer as TimerIcon, Ruler, Footprints, Activity, Percent, CheckCircle, GripVertical } from 'lucide-react';
|
||||
import { Plus, Trash2, PlayCircle, Dumbbell, Save, X, ChevronRight, List, ArrowUp, ArrowDown, Edit2, User, Flame, Timer as TimerIcon, Ruler, Footprints, Activity, Percent, CheckCircle, GripVertical, Scale } from 'lucide-react';
|
||||
import { WorkoutPlan, ExerciseDef, PlannedSet, Language, ExerciseType } from '../types';
|
||||
import { getExercises, saveExercise } from '../services/storage';
|
||||
import { t } from '../services/i18n';
|
||||
@@ -11,6 +10,8 @@ import { useActiveWorkout } from '../context/ActiveWorkoutContext';
|
||||
|
||||
import FilledInput from './FilledInput';
|
||||
import { toTitleCase } from '../utils/text';
|
||||
import { Button } from './ui/Button';
|
||||
import { Card } from './ui/Card';
|
||||
|
||||
interface PlansProps {
|
||||
lang: Language;
|
||||
@@ -162,26 +163,25 @@ const Plans: React.FC<PlansProps> = ({ lang }) => {
|
||||
if (isEditing) {
|
||||
return (
|
||||
<div className="h-full flex flex-col bg-surface">
|
||||
<div className="px-4 py-3 bg-surface-container border-b border-outline-variant flex justify-between items-center">
|
||||
<button onClick={() => setIsEditing(false)} className="p-2 text-on-surface-variant hover:bg-white/5 rounded-full"><X /></button>
|
||||
<div className="px-4 py-3 bg-surface-container border-b border-outline-variant flex justify-between items-center shrink-0">
|
||||
<Button onClick={() => setIsEditing(false)} variant="ghost" size="icon">
|
||||
<X size={20} />
|
||||
</Button>
|
||||
<h2 className="text-title-medium font-medium text-on-surface">{t('plan_editor', lang)}</h2>
|
||||
<button onClick={handleSave} className="p-2 text-primary font-medium">
|
||||
<Button onClick={handleSave} variant="ghost" className="text-primary font-medium hover:bg-primary-container/10">
|
||||
{t('save', lang)}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-6">
|
||||
<div className="bg-surface-container-high rounded-t-lg border-b border-outline-variant px-4 py-2">
|
||||
<label className="text-[10px] text-on-surface-variant font-medium">{t('ex_name', lang)}</label>
|
||||
<input
|
||||
className="w-full bg-transparent text-xl text-on-surface focus:outline-none pt-1 pb-2"
|
||||
placeholder={t('plan_name_ph', lang)}
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
autoCapitalize="words"
|
||||
onBlur={() => setName(toTitleCase(name))}
|
||||
/>
|
||||
</div>
|
||||
<FilledInput
|
||||
label={t('ex_name', lang)}
|
||||
value={name}
|
||||
onChange={(e: any) => setName(e.target.value)}
|
||||
type="text"
|
||||
autocapitalize="words"
|
||||
onBlur={() => setName(toTitleCase(name))}
|
||||
/>
|
||||
|
||||
<div className="bg-surface-container-high rounded-t-lg border-b border-outline-variant px-4 py-2">
|
||||
<label className="text-[10px] text-on-surface-variant font-medium">{t('prep_title', lang)}</label>
|
||||
@@ -200,9 +200,9 @@ const Plans: React.FC<PlansProps> = ({ lang }) => {
|
||||
|
||||
<div className="space-y-2">
|
||||
{steps.map((step, idx) => (
|
||||
<div
|
||||
<Card
|
||||
key={step.id}
|
||||
className={`bg-surface-container rounded-xl p-3 flex items-center gap-3 shadow-elevation-1 cursor-move transition-all hover:bg-surface-container-high ${draggingIndex === idx ? 'opacity-50 ring-2 ring-primary bg-surface-container-high' : ''}`}
|
||||
className={`flex items-center gap-3 transition-all hover:bg-surface-container-high ${draggingIndex === idx ? 'opacity-50 ring-2 ring-primary bg-surface-container-high' : ''}`}
|
||||
draggable
|
||||
onDragStart={() => onDragStart(idx)}
|
||||
onDragEnter={() => onDragEnter(idx)}
|
||||
@@ -221,7 +221,7 @@ const Plans: React.FC<PlansProps> = ({ lang }) => {
|
||||
<div className="text-base font-medium text-on-surface">{step.exerciseName}</div>
|
||||
<label className="flex items-center gap-2 mt-1 cursor-pointer w-fit">
|
||||
<div className={`w-4 h-4 border rounded flex items-center justify-center ${step.isWeighted ? 'bg-primary border-primary' : 'border-outline'}`}>
|
||||
{step.isWeighted && <Scale size={10} className="text-on-primary" />}
|
||||
{step.isWeighted && <Dumbbell size={10} className="text-on-primary" />}
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -232,32 +232,36 @@ const Plans: React.FC<PlansProps> = ({ lang }) => {
|
||||
<span className="text-xs text-on-surface-variant">{t('weighted', lang)}</span>
|
||||
</label>
|
||||
</div>
|
||||
<button onClick={() => removeStep(step.id)} className="text-on-surface-variant hover:text-error p-2">
|
||||
<Button onClick={() => removeStep(step.id)} variant="ghost" size="icon" className="text-on-surface-variant hover:text-error hover:bg-error/10">
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</Button>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
<Button
|
||||
onClick={() => setShowExerciseSelector(true)}
|
||||
className="w-full py-4 rounded-full border border-outline text-primary font-medium flex items-center justify-center gap-2 hover:bg-primary-container/10 transition-all"
|
||||
variant="outline"
|
||||
fullWidth
|
||||
className="py-6 rounded-full border border-outline text-primary font-medium flex items-center justify-center gap-2 hover:bg-primary-container/10 transition-all h-auto"
|
||||
>
|
||||
<Plus size={20} />
|
||||
{t('add_exercise', lang)}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showExerciseSelector && (
|
||||
<div className="fixed inset-0 bg-surface z-50 flex flex-col animate-in slide-in-from-bottom-full duration-200">
|
||||
<div className="px-4 py-3 border-b border-outline-variant flex justify-between items-center bg-surface-container">
|
||||
<div className="px-4 py-3 border-b border-outline-variant flex justify-between items-center bg-surface-container shrink-0">
|
||||
<span className="font-medium text-on-surface">{t('select_exercise', lang)}</span>
|
||||
<div className="flex gap-2">
|
||||
<button onClick={() => setIsCreatingExercise(true)} className="p-2 text-primary hover:bg-primary-container/20 rounded-full">
|
||||
<Button onClick={() => setIsCreatingExercise(true)} variant="ghost" size="icon" className="text-primary hover:bg-primary-container/20">
|
||||
<Plus size={20} />
|
||||
</button>
|
||||
<button onClick={() => setShowExerciseSelector(false)}><X /></button>
|
||||
</Button>
|
||||
<Button onClick={() => setShowExerciseSelector(false)} variant="ghost" size="icon">
|
||||
<X size={20} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto p-2">
|
||||
@@ -278,9 +282,11 @@ const Plans: React.FC<PlansProps> = ({ lang }) => {
|
||||
|
||||
{isCreatingExercise && (
|
||||
<div className="fixed inset-0 bg-surface z-[60] flex flex-col animate-in slide-in-from-bottom-full duration-200">
|
||||
<div className="px-4 py-3 border-b border-outline-variant flex justify-between items-center bg-surface-container">
|
||||
<div className="px-4 py-3 border-b border-outline-variant flex justify-between items-center bg-surface-container shrink-0">
|
||||
<h3 className="text-title-medium font-medium text-on-surface">{t('create_exercise', lang)}</h3>
|
||||
<button onClick={() => setIsCreatingExercise(false)} className="p-2 text-on-surface-variant hover:bg-white/5 rounded-full"><X /></button>
|
||||
<Button onClick={() => setIsCreatingExercise(false)} variant="ghost" size="icon" className="text-on-surface-variant hover:bg-white/5">
|
||||
<X size={20} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="p-4 space-y-6 overflow-y-auto flex-1">
|
||||
@@ -330,13 +336,14 @@ const Plans: React.FC<PlansProps> = ({ lang }) => {
|
||||
)}
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
<Button
|
||||
onClick={handleCreateExercise}
|
||||
className="w-full h-14 bg-primary text-on-primary rounded-full font-medium shadow-elevation-1 flex items-center justify-center gap-2"
|
||||
fullWidth
|
||||
size="lg"
|
||||
>
|
||||
<CheckCircle size={20} />
|
||||
<CheckCircle size={20} className="mr-2" />
|
||||
{t('create_btn', lang)}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -349,7 +356,7 @@ const Plans: React.FC<PlansProps> = ({ lang }) => {
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col bg-surface relative">
|
||||
<div className="p-4 bg-surface-container shadow-elevation-1 z-10">
|
||||
<div className="p-4 bg-surface-container shadow-elevation-1 z-10 shrink-0">
|
||||
<h2 className="text-2xl font-normal text-on-surface">{t('my_plans', lang)}</h2>
|
||||
</div>
|
||||
|
||||
@@ -363,23 +370,27 @@ const Plans: React.FC<PlansProps> = ({ lang }) => {
|
||||
</div>
|
||||
) : (
|
||||
plans.map(plan => (
|
||||
<div key={plan.id} className="bg-surface-container rounded-xl p-4 shadow-elevation-1 border border-outline-variant/20 relative overflow-hidden">
|
||||
<Card key={plan.id} className="relative overflow-hidden">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<h3 className="text-xl font-normal text-on-surface">{plan.name}</h3>
|
||||
<button
|
||||
<Button
|
||||
onClick={(e) => handleDelete(plan.id, e)}
|
||||
className="text-on-surface-variant hover:text-error p-2 rounded-full hover:bg-white/5"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-on-surface-variant hover:text-error hover:bg-white/5"
|
||||
>
|
||||
<Trash2 size={20} />
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="absolute top-4 right-14">
|
||||
<button
|
||||
<Button
|
||||
onClick={(e) => { e.stopPropagation(); handleEdit(plan); }}
|
||||
className="text-on-surface-variant hover:text-primary p-2 rounded-full hover:bg-white/5"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-on-surface-variant hover:text-primary hover:bg-white/5"
|
||||
>
|
||||
<Edit2 size={20} />
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-on-surface-variant text-sm line-clamp-2 mb-4 min-h-[1.25rem]">
|
||||
{plan.description || t('prep_no_instructions', lang)}
|
||||
@@ -388,15 +399,15 @@ const Plans: React.FC<PlansProps> = ({ lang }) => {
|
||||
<div className="text-xs font-medium text-primary bg-primary-container/20 px-3 py-1 rounded-full">
|
||||
{plan.steps.length} {t('exercises_count', lang)}
|
||||
</div>
|
||||
<button
|
||||
<Button
|
||||
onClick={() => startSession(plan)}
|
||||
className="flex items-center gap-2 bg-primary text-on-primary px-5 py-2 rounded-full text-sm font-medium hover:shadow-elevation-2 transition-all"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<PlayCircle size={18} />
|
||||
{t('start', lang)}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user