1. Session end time saving. 2. Plan Id to the saved session. 3. History page redesigned (attributes moved, sets list hidden. 4. Session duration added. 5. Session start and end time logging fixed.

This commit is contained in:
AG
2025-11-24 23:22:09 +02:00
parent cce1e58c7b
commit 72867668d4
7 changed files with 116 additions and 64 deletions

View File

@@ -41,6 +41,23 @@ const History: React.FC<HistoryProps> = ({ sessions, onUpdateSession, onDeleteSe
return new Date(value).getTime();
};
const formatDuration = (startTime: number, endTime?: number) => {
if (!endTime || isNaN(endTime) || isNaN(startTime)) return '';
const durationMs = endTime - startTime;
if (durationMs < 0 || isNaN(durationMs)) return '';
const hours = Math.floor(durationMs / 3600000);
const minutes = Math.floor((durationMs % 3600000) / 60000);
if (hours > 0) {
return `${hours}h ${minutes}m`;
}
if (minutes < 1) {
return '<1m';
}
return `${minutes}m`;
};
const handleSaveEdit = () => {
if (editingSession && onUpdateSession) {
onUpdateSession(editingSession);
@@ -91,81 +108,66 @@ const History: React.FC<HistoryProps> = ({ sessions, onUpdateSession, onDeleteSe
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">
<div className="flex justify-between items-start mb-4 border-b border-outline-variant pb-3">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-tertiary-container text-on-tertiary-container flex items-center justify-center">
<Calendar size={20} />
<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 && (
<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>
<div className="font-medium text-on-surface text-lg">
{new Date(session.startTime).toLocaleDateString(lang === 'ru' ? 'ru-RU' : 'en-US', { weekday: 'long', day: 'numeric', month: 'long' })}
</div>
<div className="text-xs text-on-surface-variant flex items-center gap-2">
<span>{new Date(session.startTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>
{session.userBodyWeight && <span className="px-2 py-0.5 rounded-full bg-surface-container-high text-on-surface">{session.userBodyWeight}kg</span>}
</div>
<div className="mt-2 text-xs text-on-surface-variant">
{t('sets_count', lang)}: <span className="text-on-surface font-medium">{session.sets.length}</span>
{totalWork > 0 && (
<span className="ml-4 inline-flex items-center gap-1">
<Scale size={12} />
{(totalWork / 1000).toFixed(1)}t
</span>
)}
</div>
</div>
<div className="flex gap-1">
<button
onClick={() => setEditingSession(JSON.parse(JSON.stringify(session)))}
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={() => setDeletingId(session.id)}
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 className="space-y-2">
{Array.from(new Set(session.sets.map(s => s.exerciseName))).slice(0, 4).map(exName => {
const sets = session.sets.filter(s => s.exerciseName === exName);
const count = sets.length;
const bestSet = sets[0];
let detail = "";
if (bestSet.type === ExerciseType.HIGH_JUMP) detail = `${t('max', lang)}: ${Math.max(...sets.map(s => s.height || 0))}cm`;
else if (bestSet.type === ExerciseType.LONG_JUMP) detail = `${t('max', lang)}: ${Math.max(...sets.map(s => s.distanceMeters || 0))}m`;
else if (bestSet.type === ExerciseType.STRENGTH) detail = `${t('upto', lang)} ${Math.max(...sets.map(s => s.weight || 0))}kg`;
return (
<div key={`${session.id}-${exName}`} className="flex justify-between text-sm items-center">
<span className="text-on-surface">{exName}</span>
<span className="text-on-surface-variant flex gap-2 items-center">
{detail && <span className="text-[10px] bg-surface-container-high px-2 py-0.5 rounded text-on-surface-variant">{detail}</span>}
<span>{count}</span>
</span>
</div>
);
})}
{new Set(session.sets.map(s => s.exerciseName)).size > 4 && (
<div className="text-xs text-center text-on-surface-variant mt-2">
+ ...
</div>
)}
</div>
<div className="mt-4 pt-3 border-t border-outline-variant flex justify-between items-center">
<div className="flex gap-4">
<span className="text-xs text-on-surface-variant">{t('sets_count', lang)}: <span className="text-on-surface font-medium">{session.sets.length}</span></span>
{totalWork > 0 && (
<span className="text-xs text-on-surface-variant flex items-center gap-1">
<Scale size={12} />
{(totalWork / 1000).toFixed(1)}t
</span>
)}
</div>
<div className="flex items-center gap-1 text-primary text-xs font-bold tracking-wide">
<TrendingUp size={14} />
{t('finished', lang)}
</div>
</div>
</div>
)
})}
@@ -251,8 +253,12 @@ const History: React.FC<HistoryProps> = ({ sessions, onUpdateSession, onDeleteSe
<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}</span>
</div>
<button onClick={() => handleDeleteSet(set.id)} className="text-on-surface-variant hover:text-error">
<X size={18} />
<button
onClick={() => handleDeleteSet(set.id)}
className="text-on-surface-variant hover:text-error p-1 rounded hover:bg-error-container/10 transition-colors"
title={t('delete', lang)}
>
<Trash2 size={18} />
</button>
</div>
@@ -290,7 +296,39 @@ const History: React.FC<HistoryProps> = ({ sessions, onUpdateSession, onDeleteSe
/>
</div>
)}
{/* Add other fields similarly styled if needed */}
{(set.type === ExerciseType.CARDIO || 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"><Timer size={10} /> {t('time_sec', lang)}</label>
<input
type="number"
className="w-full bg-transparent text-sm text-on-surface focus:outline-none"
value={set.durationSeconds ?? ''}
onChange={(e) => handleUpdateSet(set.id, 'durationSeconds', parseFloat(e.target.value))}
/>
</div>
)}
{(set.type === ExerciseType.CARDIO || set.type === ExerciseType.LONG_JUMP) && (
<div className="bg-surface-container-high rounded px-2 py-1">
<label className="text-[10px] text-on-surface-variant flex gap-1 items-center"><ArrowRight size={10} /> {t('dist_m', lang)}</label>
<input
type="number"
className="w-full bg-transparent text-sm text-on-surface focus:outline-none"
value={set.distanceMeters ?? ''}
onChange={(e) => handleUpdateSet(set.id, 'distanceMeters', parseFloat(e.target.value))}
/>
</div>
)}
{(set.type === ExerciseType.HIGH_JUMP) && (
<div className="bg-surface-container-high rounded px-2 py-1">
<label className="text-[10px] text-on-surface-variant flex gap-1 items-center"><ArrowUp size={10} /> {t('height_cm', lang)}</label>
<input
type="number"
className="w-full bg-transparent text-sm text-on-surface focus:outline-none"
value={set.height ?? ''}
onChange={(e) => handleUpdateSet(set.id, 'height', parseFloat(e.target.value))}
/>
</div>
)}
</div>
</div>
))}