UI refactoring: Profile, History, and Plans Components
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Calendar, Clock, TrendingUp, Gauge, Pencil, Trash2, X, Save, ArrowRight, ArrowUp, Timer, Activity, Dumbbell, Percent } from 'lucide-react';
|
||||
import { WorkoutSession, ExerciseType, WorkoutSet, Language } from '../types';
|
||||
import { t } from '../services/i18n';
|
||||
import { useSession } from '../context/SessionContext';
|
||||
import { Button } from './ui/Button';
|
||||
import { Card } from './ui/Card';
|
||||
import { Modal } from './ui/Modal';
|
||||
import FilledInput from './FilledInput';
|
||||
|
||||
interface HistoryProps {
|
||||
lang: Language;
|
||||
@@ -116,8 +119,6 @@ const History: React.FC<HistoryProps> = ({ lang }) => {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (sessions.length === 0) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full text-on-surface-variant p-8 text-center">
|
||||
@@ -129,257 +130,268 @@ const History: React.FC<HistoryProps> = ({ lang }) => {
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col bg-surface">
|
||||
<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('tab_history', lang)}</h2>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-4 pb-20">
|
||||
{/* Regular Workout Sessions */}
|
||||
{sessions.filter(s => s.type === 'STANDARD').map((session) => {
|
||||
const totalWork = calculateSessionWork(session);
|
||||
<div className="flex-1 overflow-y-auto p-4 pb-20">
|
||||
<div className="max-w-2xl mx-auto space-y-4">
|
||||
{/* Regular Workout Sessions */}
|
||||
{sessions.filter(s => s.type === 'STANDARD').map((session) => {
|
||||
const totalWork = calculateSessionWork(session);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={session.id}
|
||||
className="bg-surface-container rounded-xl p-5 shadow-elevation-1 border border-outline-variant/20 cursor-pointer hover:bg-surface-container-high transition-colors"
|
||||
onClick={() => setEditingSession(JSON.parse(JSON.stringify(session)))}
|
||||
>
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<span className="font-medium text-on-surface text-lg">
|
||||
{new Date(session.startTime).toISOString().split('T')[0]}
|
||||
</span>
|
||||
<span className="text-sm text-on-surface-variant">
|
||||
{new Date(session.startTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
{session.endTime && (
|
||||
return (
|
||||
<Card
|
||||
key={session.id}
|
||||
className="cursor-pointer hover:bg-surface-container-high transition-colors"
|
||||
onClick={() => setEditingSession(JSON.parse(JSON.stringify(session)))}
|
||||
>
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<span className="font-medium text-on-surface text-lg">
|
||||
{new Date(session.startTime).toISOString().split('T')[0]}
|
||||
</span>
|
||||
<span className="text-sm text-on-surface-variant">
|
||||
{formatDuration(session.startTime, session.endTime)}
|
||||
{new Date(session.startTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-sm text-on-surface-variant">
|
||||
{session.planName || t('no_plan', lang)}
|
||||
</span>
|
||||
{session.userBodyWeight && (
|
||||
<span className="px-2 py-0.5 rounded-full bg-surface-container-high text-on-surface text-xs">
|
||||
{session.userBodyWeight}kg
|
||||
{session.endTime && (
|
||||
<span className="text-sm text-on-surface-variant">
|
||||
{formatDuration(session.startTime, session.endTime)}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-sm text-on-surface-variant">
|
||||
{session.planName || t('no_plan', lang)}
|
||||
</span>
|
||||
)}
|
||||
{session.userBodyWeight && (
|
||||
<span className="px-2 py-0.5 rounded-full bg-surface-container-high text-on-surface text-xs">
|
||||
{session.userBodyWeight}kg
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-on-surface-variant flex items-center">
|
||||
<span className="inline-flex items-center">
|
||||
{t('sets_count', lang)}: <span className="text-on-surface font-medium ml-1">{session.sets.length}</span>
|
||||
</span>
|
||||
{totalWork > 0 && (
|
||||
<span className="ml-4 inline-flex items-center gap-1">
|
||||
<Gauge size={12} />
|
||||
{(totalWork / 1000).toFixed(1)}t
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-on-surface-variant flex items-center">
|
||||
<span className="inline-flex items-center">
|
||||
{t('sets_count', lang)}: <span className="text-on-surface font-medium ml-1">{session.sets.length}</span>
|
||||
</span>
|
||||
{totalWork > 0 && (
|
||||
<span className="ml-4 inline-flex items-center gap-1">
|
||||
<Gauge size={12} />
|
||||
{(totalWork / 1000).toFixed(1)}t
|
||||
</span>
|
||||
)}
|
||||
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setEditingSession(JSON.parse(JSON.stringify(session)));
|
||||
}}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-on-surface-variant hover:text-primary"
|
||||
>
|
||||
<Pencil size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setDeletingId(session.id);
|
||||
}}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-on-surface-variant hover:text-error"
|
||||
>
|
||||
<Trash2 size={20} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setEditingSession(JSON.parse(JSON.stringify(session)));
|
||||
}}
|
||||
className="p-2 text-on-surface-variant hover:text-primary hover:bg-surface-container-high rounded-full transition-colors"
|
||||
>
|
||||
<Pencil size={20} />
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setDeletingId(session.id);
|
||||
}}
|
||||
className="p-2 text-on-surface-variant hover:text-error hover:bg-surface-container-high rounded-full transition-colors"
|
||||
>
|
||||
<Trash2 size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* Quick Log Sessions */}
|
||||
{sessions.filter(s => s.type === 'QUICK_LOG').length > 0 && (
|
||||
<div className="mt-8">
|
||||
<h3 className="text-xl font-medium text-on-surface mb-4 px-2">{t('quick_log', lang)}</h3>
|
||||
{Object.entries(
|
||||
sessions
|
||||
.filter(s => s.type === 'QUICK_LOG')
|
||||
.reduce((groups: Record<string, WorkoutSession[]>, session) => {
|
||||
const date = new Date(session.startTime).toISOString().split('T')[0];
|
||||
if (!groups[date]) groups[date] = [];
|
||||
groups[date].push(session);
|
||||
return groups;
|
||||
}, {})
|
||||
</Card>
|
||||
)
|
||||
.sort(([a], [b]) => b.localeCompare(a))
|
||||
.map(([date, daySessions]) => (
|
||||
<div key={date} className="mb-4">
|
||||
<div className="text-sm text-on-surface-variant px-2 mb-2 font-medium">{date}</div>
|
||||
<div className="space-y-2">
|
||||
{daySessions.flatMap(session => session.sets).map((set, idx) => (
|
||||
<div
|
||||
key={set.id}
|
||||
className="bg-surface-container-low rounded-xl p-4 border border-outline-variant/10 flex justify-between items-center"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-on-surface">
|
||||
{set.exerciseName}
|
||||
{set.side && <span className="ml-2 text-xs font-medium text-on-surface-variant">{t(set.side.toLowerCase() as any, lang)}</span>}
|
||||
</div>
|
||||
<div className="text-sm text-on-surface-variant mt-1">
|
||||
{set.type === ExerciseType.STRENGTH && `${set.weight || 0}kg x ${set.reps || 0}`}
|
||||
{set.type === ExerciseType.BODYWEIGHT && `${set.weight ? `+${set.weight}kg` : 'BW'} x ${set.reps || 0}`}
|
||||
{set.type === ExerciseType.CARDIO && `${set.durationSeconds || 0}s ${set.distanceMeters ? `/ ${set.distanceMeters}m` : ''}`}
|
||||
{set.type === ExerciseType.STATIC && `${set.durationSeconds || 0}s`}
|
||||
{set.type === ExerciseType.HIGH_JUMP && `${set.height || 0}cm`}
|
||||
{set.type === ExerciseType.LONG_JUMP && `${set.distanceMeters || 0}m`}
|
||||
{set.type === ExerciseType.PLYOMETRIC && `x ${set.reps || 0}`}
|
||||
</div>
|
||||
<div className="text-xs text-on-surface-variant mt-1">
|
||||
{new Date(set.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
onClick={() => {
|
||||
// Find the session this set belongs to and open edit mode
|
||||
const parentSession = daySessions.find(s => s.sets.some(st => st.id === set.id));
|
||||
if (parentSession) {
|
||||
setEditingSession(JSON.parse(JSON.stringify(parentSession)));
|
||||
}
|
||||
}}
|
||||
className="p-2 text-on-surface-variant hover:text-primary hover:bg-surface-container-high rounded-full transition-colors"
|
||||
>
|
||||
<Pencil size={18} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
// Find the session and set up for deletion
|
||||
const parentSession = daySessions.find(s => s.sets.some(st => st.id === set.id));
|
||||
if (parentSession) {
|
||||
setDeletingSetInfo({ sessionId: parentSession.id, setId: set.id });
|
||||
}
|
||||
}}
|
||||
className="p-2 text-on-surface-variant hover:text-error hover:bg-surface-container-high rounded-full transition-colors"
|
||||
>
|
||||
<Trash2 size={18} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
})}
|
||||
|
||||
{/* Quick Log Sessions */}
|
||||
{sessions.filter(s => s.type === 'QUICK_LOG').length > 0 && (
|
||||
<div className="mt-8">
|
||||
<h3 className="text-xl font-medium text-on-surface mb-4 px-2">{t('quick_log', lang)}</h3>
|
||||
{(Object.entries(
|
||||
sessions
|
||||
.filter(s => s.type === 'QUICK_LOG')
|
||||
.reduce<Record<string, WorkoutSession[]>>((groups, session) => {
|
||||
const date = new Date(session.startTime).toISOString().split('T')[0];
|
||||
if (!groups[date]) groups[date] = [];
|
||||
groups[date].push(session);
|
||||
return groups;
|
||||
}, {})
|
||||
) as [string, WorkoutSession[]][])
|
||||
.sort(([a], [b]) => b.localeCompare(a))
|
||||
.map(([date, daySessions]) => (
|
||||
<div key={date} className="mb-4">
|
||||
<div className="text-sm text-on-surface-variant px-2 mb-2 font-medium">{date}</div>
|
||||
<div className="space-y-2">
|
||||
{daySessions
|
||||
.reduce<WorkoutSet[]>((acc, session) => acc.concat(session.sets), [])
|
||||
.map((set, idx) => (
|
||||
<Card
|
||||
key={set.id}
|
||||
className="bg-surface-container-low flex justify-between items-center"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-on-surface">
|
||||
{set.exerciseName}
|
||||
{set.side && <span className="ml-2 text-xs font-medium text-on-surface-variant">{t(set.side.toLowerCase() as any, lang)}</span>}
|
||||
</div>
|
||||
<div className="text-sm text-on-surface-variant mt-1">
|
||||
{set.type === ExerciseType.STRENGTH && `${set.weight || 0}kg x ${set.reps || 0}`}
|
||||
{set.type === ExerciseType.BODYWEIGHT && `${set.weight ? `+${set.weight}kg` : 'BW'} x ${set.reps || 0}`}
|
||||
{set.type === ExerciseType.CARDIO && `${set.durationSeconds || 0}s ${set.distanceMeters ? `/ ${set.distanceMeters}m` : ''}`}
|
||||
{set.type === ExerciseType.STATIC && `${set.durationSeconds || 0}s`}
|
||||
{set.type === ExerciseType.HIGH_JUMP && `${set.height || 0}cm`}
|
||||
{set.type === ExerciseType.LONG_JUMP && `${set.distanceMeters || 0}m`}
|
||||
{set.type === ExerciseType.PLYOMETRIC && `x ${set.reps || 0}`}
|
||||
</div>
|
||||
<div className="text-xs text-on-surface-variant mt-1">
|
||||
{new Date(set.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
onClick={() => {
|
||||
// Find the session this set belongs to and open edit mode
|
||||
const parentSession = daySessions.find(s => s.sets.some(st => st.id === set.id));
|
||||
if (parentSession) {
|
||||
setEditingSession(JSON.parse(JSON.stringify(parentSession)));
|
||||
}
|
||||
}}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
>
|
||||
<Pencil size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
// Find the session and set up for deletion
|
||||
const parentSession = daySessions.find(s => s.sets.some(st => st.id === set.id));
|
||||
if (parentSession) {
|
||||
setDeletingSetInfo({ sessionId: parentSession.id, setId: set.id });
|
||||
}
|
||||
}}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 text-error hover:text-error"
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* DELETE CONFIRMATION DIALOG (MD3) */}
|
||||
{(deletingId || deletingSetInfo) && (
|
||||
<div className="fixed inset-0 z-[60] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
|
||||
<div className="bg-surface-container w-full max-w-xs rounded-[28px] p-6 shadow-elevation-3">
|
||||
<h3 className="text-xl font-normal text-on-surface mb-2">
|
||||
{deletingId ? t('delete_workout', lang) : t('delete_set', lang) || 'Delete Set'}
|
||||
</h3>
|
||||
<p className="text-sm text-on-surface-variant mb-8">
|
||||
{deletingId ? t('delete_confirm', lang) : t('delete_set_confirm', lang) || 'Are you sure you want to delete this set?'}
|
||||
</p>
|
||||
<div className="flex justify-end gap-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
setDeletingId(null);
|
||||
setDeletingSetInfo(null);
|
||||
}}
|
||||
className="px-4 py-2 rounded-full text-primary font-medium hover:bg-white/5"
|
||||
>
|
||||
{t('cancel', lang)}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirmDelete}
|
||||
className="px-4 py-2 rounded-full bg-error-container text-on-error-container font-medium"
|
||||
>
|
||||
{t('delete', lang)}
|
||||
</button>
|
||||
</div>
|
||||
{/* DELETE CONFIRMATION MODAL */}
|
||||
<Modal
|
||||
isOpen={!!(deletingId || deletingSetInfo)}
|
||||
onClose={() => {
|
||||
setDeletingId(null);
|
||||
setDeletingSetInfo(null);
|
||||
}}
|
||||
title={deletingId ? t('delete_workout', lang) : t('delete_set', lang) || 'Delete Set'}
|
||||
maxWidth="sm"
|
||||
>
|
||||
<div className="space-y-6">
|
||||
<p className="text-sm text-on-surface-variant">
|
||||
{deletingId ? t('delete_confirm', lang) : t('delete_set_confirm', lang) || 'Are you sure you want to delete this set?'}
|
||||
</p>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setDeletingId(null);
|
||||
setDeletingSetInfo(null);
|
||||
}}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
>
|
||||
{t('cancel', lang)}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleConfirmDelete}
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
>
|
||||
{t('delete', lang)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
{/* EDIT SESSION FULLSCREEN DIALOG */}
|
||||
{/* EDIT SESSION MODAL */}
|
||||
{editingSession && (
|
||||
<div className="fixed inset-0 z-[60] bg-surface flex flex-col animate-in slide-in-from-bottom-10 duration-200">
|
||||
<div className="px-4 py-3 border-b border-outline-variant flex justify-between items-center bg-surface-container shadow-elevation-1">
|
||||
<button onClick={() => setEditingSession(null)} className="text-on-surface-variant hover:text-on-surface">
|
||||
<X />
|
||||
</button>
|
||||
<h2 className="text-lg font-medium text-on-surface">{t('edit', lang)}</h2>
|
||||
<button onClick={handleSaveEdit} className="text-primary font-medium flex items-center gap-2">
|
||||
{t('save', lang)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-6">
|
||||
<Modal
|
||||
isOpen={!!editingSession}
|
||||
onClose={() => setEditingSession(null)}
|
||||
title={t('edit', lang)}
|
||||
maxWidth="lg"
|
||||
>
|
||||
<div className="space-y-6">
|
||||
{/* Meta Info */}
|
||||
<div className="bg-surface-container p-4 rounded-xl border border-outline-variant/20 space-y-4">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="bg-surface-container-high rounded-t-lg px-3 py-2 border-b border-outline-variant">
|
||||
<label className="text-[10px] text-on-surface-variant font-bold block">{t('start_time', lang)}</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={formatDateForInput(editingSession.startTime)}
|
||||
onChange={(e) => setEditingSession({ ...editingSession, startTime: parseDateFromInput(e.target.value) })}
|
||||
className="w-full bg-transparent text-on-surface focus:outline-none text-sm mt-1"
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-surface-container-high rounded-t-lg px-3 py-2 border-b border-outline-variant">
|
||||
<label className="text-[10px] text-on-surface-variant font-bold block">{t('end_time', lang)}</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={editingSession.endTime ? formatDateForInput(editingSession.endTime) : ''}
|
||||
onChange={(e) => setEditingSession({ ...editingSession, endTime: parseDateFromInput(e.target.value) })}
|
||||
className="w-full bg-transparent text-on-surface focus:outline-none text-sm mt-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="bg-surface-container-high rounded-t-lg px-3 py-2 border-b border-outline-variant">
|
||||
<label className="text-[10px] text-on-surface-variant font-bold block">{t('weight_kg', lang)}</label>
|
||||
<label className="text-[10px] text-on-surface-variant font-bold block">{t('start_time', lang)}</label>
|
||||
<input
|
||||
type="number"
|
||||
value={editingSession.userBodyWeight || ''}
|
||||
onChange={(e) => setEditingSession({ ...editingSession, userBodyWeight: parseFloat(e.target.value) })}
|
||||
className="w-full bg-transparent text-on-surface focus:outline-none text-lg mt-1"
|
||||
type="datetime-local"
|
||||
value={formatDateForInput(editingSession.startTime)}
|
||||
onChange={(e) => setEditingSession({ ...editingSession, startTime: parseDateFromInput(e.target.value) })}
|
||||
className="w-full bg-transparent text-on-surface focus:outline-none text-sm mt-1"
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-surface-container-high rounded-t-lg px-3 py-2 border-b border-outline-variant">
|
||||
<label className="text-[10px] text-on-surface-variant font-bold block">{t('end_time', lang)}</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={editingSession.endTime ? formatDateForInput(editingSession.endTime) : ''}
|
||||
onChange={(e) => setEditingSession({ ...editingSession, endTime: parseDateFromInput(e.target.value) })}
|
||||
className="w-full bg-transparent text-on-surface focus:outline-none text-sm mt-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-surface-container-high rounded-t-lg px-3 py-2 border-b border-outline-variant">
|
||||
<label className="text-[10px] text-on-surface-variant font-bold block">{t('weight_kg', lang)}</label>
|
||||
<input
|
||||
type="number"
|
||||
value={editingSession.userBodyWeight || ''}
|
||||
onChange={(e) => setEditingSession({ ...editingSession, userBodyWeight: parseFloat(e.target.value) })}
|
||||
className="w-full bg-transparent text-on-surface focus:outline-none text-lg mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-medium text-primary ml-1">{t('sets_count', lang)} ({editingSession.sets.length})</h3>
|
||||
{editingSession.sets.map((set, idx) => (
|
||||
<div key={set.id} className="bg-surface-container p-3 rounded-xl border border-outline-variant/20 flex flex-col gap-3 shadow-sm">
|
||||
<div className="flex justify-between items-center border-b border-outline-variant pb-2">
|
||||
<div key={set.id} className="bg-surface-container-low p-3 rounded-xl border border-outline-variant/20 flex flex-col gap-3">
|
||||
<div className="flex justify-between items-center border-b border-outline-variant/50 pb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-6 h-6 rounded-full bg-secondary-container text-on-secondary-container text-xs font-bold flex items-center justify-center">{idx + 1}</span>
|
||||
<span className="font-medium text-on-surface text-sm">{set.exerciseName}{set.side && <span className="ml-2 text-xs font-medium text-on-surface-variant">{t(set.side.toLowerCase(), lang)}</span>}</span>
|
||||
<span className="font-medium text-on-surface text-sm">{set.exerciseName}{set.side && <span className="ml-2 text-xs font-medium text-on-surface-variant">{t(set.side.toLowerCase() as any, lang)}</span>}</span>
|
||||
</div>
|
||||
<button
|
||||
<Button
|
||||
onClick={() => handleDeleteSet(set.id)}
|
||||
className="text-on-surface-variant hover:text-error p-1 rounded hover:bg-error-container/10 transition-colors"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 text-on-surface-variant hover:text-error hover:bg-error-container/10"
|
||||
title={t('delete', lang)}
|
||||
>
|
||||
<Trash2 size={18} />
|
||||
</button>
|
||||
<Trash2 size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
|
||||
{(set.type === ExerciseType.STRENGTH || set.type === ExerciseType.BODYWEIGHT || set.type === ExerciseType.STATIC) && (
|
||||
<div className="bg-surface-container-high rounded px-2 py-1">
|
||||
<label className="text-[10px] text-on-surface-variant flex gap-1 items-center"><Dumbbell size={10} /> {t('weight_kg', lang)}</label>
|
||||
@@ -450,11 +462,16 @@ const History: React.FC<HistoryProps> = ({ lang }) => {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end pt-4 border-t border-outline-variant">
|
||||
<Button onClick={handleSaveEdit}>
|
||||
<Save size={16} className="mr-2" />
|
||||
{t('save', lang)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user