History added to Quick Log. Some minor fixes.
This commit is contained in:
1
App.tsx
1
App.tsx
@@ -235,6 +235,7 @@ function App() {
|
|||||||
userWeight={currentUser.profile?.weight}
|
userWeight={currentUser.profile?.weight}
|
||||||
activeSession={activeSession}
|
activeSession={activeSession}
|
||||||
activePlan={activePlan}
|
activePlan={activePlan}
|
||||||
|
sporadicSets={sporadicSets}
|
||||||
onSessionStart={handleStartSession}
|
onSessionStart={handleStartSession}
|
||||||
onSessionEnd={handleEndSession}
|
onSessionEnd={handleEndSession}
|
||||||
onSessionQuit={handleQuitSession}
|
onSessionQuit={handleQuitSession}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Dumbbell, Scale, Activity, Timer as TimerIcon, ArrowRight, ArrowUp, Plus, CheckCircle } from 'lucide-react';
|
import { Dumbbell, Scale, Activity, Timer as TimerIcon, ArrowRight, ArrowUp, Plus, CheckCircle, Edit, Trash2 } from 'lucide-react';
|
||||||
import { ExerciseType, Language } from '../../types';
|
import { ExerciseType, Language, SporadicSet } from '../../types';
|
||||||
import { t } from '../../services/i18n';
|
import { t } from '../../services/i18n';
|
||||||
import FilledInput from '../FilledInput';
|
import FilledInput from '../FilledInput';
|
||||||
import ExerciseModal from '../ExerciseModal';
|
import ExerciseModal from '../ExerciseModal';
|
||||||
@@ -9,9 +9,10 @@ import { useTracker } from './useTracker';
|
|||||||
interface SporadicViewProps {
|
interface SporadicViewProps {
|
||||||
tracker: ReturnType<typeof useTracker>;
|
tracker: ReturnType<typeof useTracker>;
|
||||||
lang: Language;
|
lang: Language;
|
||||||
|
sporadicSets?: SporadicSet[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const SporadicView: React.FC<SporadicViewProps> = ({ tracker, lang }) => {
|
const SporadicView: React.FC<SporadicViewProps> = ({ tracker, lang, sporadicSets }) => {
|
||||||
const {
|
const {
|
||||||
searchQuery,
|
searchQuery,
|
||||||
setSearchQuery,
|
setSearchQuery,
|
||||||
@@ -40,6 +41,27 @@ const SporadicView: React.FC<SporadicViewProps> = ({ tracker, lang }) => {
|
|||||||
resetForm
|
resetForm
|
||||||
} = tracker;
|
} = tracker;
|
||||||
|
|
||||||
|
const [todaysSets, setTodaysSets] = useState<SporadicSet[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (sporadicSets) {
|
||||||
|
const startOfDay = new Date();
|
||||||
|
startOfDay.setHours(0, 0, 0, 0);
|
||||||
|
const todayS = sporadicSets.filter(s => s.timestamp >= startOfDay.getTime());
|
||||||
|
setTodaysSets(todayS.sort((a, b) => b.timestamp - a.timestamp));
|
||||||
|
}
|
||||||
|
}, [sporadicSets]);
|
||||||
|
|
||||||
|
const renderSetMetrics = (set: SporadicSet) => {
|
||||||
|
const metrics: string[] = [];
|
||||||
|
if (set.weight) metrics.push(`${set.weight} ${t('weight_kg', lang)}`);
|
||||||
|
if (set.reps) metrics.push(`${set.reps} ${t('reps', lang)}`);
|
||||||
|
if (set.durationSeconds) metrics.push(`${set.durationSeconds} ${t('time_sec', lang)}`);
|
||||||
|
if (set.distanceMeters) metrics.push(`${set.distanceMeters} ${t('dist_m', lang)}`);
|
||||||
|
if (set.height) metrics.push(`${set.height} ${t('height_cm', lang)}`);
|
||||||
|
return metrics.join(' / ');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full max-h-full overflow-hidden relative bg-surface">
|
<div className="flex flex-col h-full max-h-full overflow-hidden relative bg-surface">
|
||||||
<div className="px-4 py-3 bg-surface-container shadow-elevation-1 z-20 flex justify-between items-center">
|
<div className="px-4 py-3 bg-surface-container shadow-elevation-1 z-20 flex justify-between items-center">
|
||||||
@@ -50,7 +72,7 @@ const SporadicView: React.FC<SporadicViewProps> = ({ tracker, lang }) => {
|
|||||||
}}
|
}}
|
||||||
className="text-error font-medium text-sm hover:opacity-80 transition-opacity"
|
className="text-error font-medium text-sm hover:opacity-80 transition-opacity"
|
||||||
>
|
>
|
||||||
{t('cancel', lang)}
|
{t('quit', lang)}
|
||||||
</button>
|
</button>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<h2 className="text-title-medium text-on-surface flex items-center gap-2 font-medium">
|
<h2 className="text-title-medium text-on-surface flex items-center gap-2 font-medium">
|
||||||
@@ -102,7 +124,7 @@ const SporadicView: React.FC<SporadicViewProps> = ({ tracker, lang }) => {
|
|||||||
<button
|
<button
|
||||||
key={ex.id}
|
key={ex.id}
|
||||||
onMouseDown={(e) => {
|
onMouseDown={(e) => {
|
||||||
e.preventDefault(); // Prevent input blur
|
e.preventDefault();
|
||||||
setSelectedExercise(ex);
|
setSelectedExercise(ex);
|
||||||
setSearchQuery(ex.name);
|
setSearchQuery(ex.name);
|
||||||
setShowSuggestions(false);
|
setShowSuggestions(false);
|
||||||
@@ -178,6 +200,26 @@ const SporadicView: React.FC<SporadicViewProps> = ({ tracker, lang }) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* History Section */}
|
||||||
|
{todaysSets.length > 0 && (
|
||||||
|
<div className="mt-6">
|
||||||
|
<h3 className="text-title-medium font-medium mb-3">{t('history_section', lang)}</h3>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{todaysSets.map(set => (
|
||||||
|
<div key={set.id} className="bg-surface-container rounded-lg p-3 flex items-center justify-between shadow-elevation-1 animate-in fade-in">
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-on-surface">{set.exerciseName}</p>
|
||||||
|
<p className="text-sm text-on-surface-variant">{renderSetMetrics(set)}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{/* Edit and Delete buttons can be added here in the future */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isCreating && (
|
{isCreating && (
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { WorkoutSession, WorkoutSet, WorkoutPlan, Language } from '../../types';
|
import { WorkoutSession, WorkoutSet, WorkoutPlan, Language, SporadicSet } from '../../types';
|
||||||
import { useTracker } from './useTracker';
|
import { useTracker } from './useTracker';
|
||||||
import IdleView from './IdleView';
|
import IdleView from './IdleView';
|
||||||
import SporadicView from './SporadicView';
|
import SporadicView from './SporadicView';
|
||||||
@@ -10,6 +11,7 @@ interface TrackerProps {
|
|||||||
userWeight?: number;
|
userWeight?: number;
|
||||||
activeSession: WorkoutSession | null;
|
activeSession: WorkoutSession | null;
|
||||||
activePlan: WorkoutPlan | null;
|
activePlan: WorkoutPlan | null;
|
||||||
|
sporadicSets?: SporadicSet[];
|
||||||
onSessionStart: (plan?: WorkoutPlan, startWeight?: number) => void;
|
onSessionStart: (plan?: WorkoutPlan, startWeight?: number) => void;
|
||||||
onSessionEnd: () => void;
|
onSessionEnd: () => void;
|
||||||
onSessionQuit: () => void;
|
onSessionQuit: () => void;
|
||||||
@@ -23,7 +25,7 @@ interface TrackerProps {
|
|||||||
const Tracker: React.FC<TrackerProps> = (props) => {
|
const Tracker: React.FC<TrackerProps> = (props) => {
|
||||||
const tracker = useTracker(props);
|
const tracker = useTracker(props);
|
||||||
const { isSporadicMode } = tracker;
|
const { isSporadicMode } = tracker;
|
||||||
const { activeSession, lang, onSessionEnd, onSessionQuit, onRemoveSet } = props;
|
const { activeSession, lang, onSessionEnd, onSessionQuit, onRemoveSet, sporadicSets } = props;
|
||||||
|
|
||||||
if (activeSession) {
|
if (activeSession) {
|
||||||
return (
|
return (
|
||||||
@@ -39,7 +41,7 @@ const Tracker: React.FC<TrackerProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isSporadicMode) {
|
if (isSporadicMode) {
|
||||||
return <SporadicView tracker={tracker} lang={lang} />;
|
return <SporadicView tracker={tracker} lang={lang} sporadicSets={sporadicSets} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <IdleView tracker={tracker} lang={lang} />;
|
return <IdleView tracker={tracker} lang={lang} />;
|
||||||
|
|||||||
Binary file not shown.
@@ -48,6 +48,7 @@ const translations = {
|
|||||||
prep_title: 'Preparation',
|
prep_title: 'Preparation',
|
||||||
prep_no_instructions: 'No specific instructions.',
|
prep_no_instructions: 'No specific instructions.',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
|
quit: 'Quit',
|
||||||
start: 'Start',
|
start: 'Start',
|
||||||
finish: 'Finish',
|
finish: 'Finish',
|
||||||
finish_confirm_title: 'Finish Workout?',
|
finish_confirm_title: 'Finish Workout?',
|
||||||
@@ -104,7 +105,7 @@ const translations = {
|
|||||||
// Plans
|
// Plans
|
||||||
plans_empty: 'No plans created',
|
plans_empty: 'No plans created',
|
||||||
plan_editor: 'Plan Editor',
|
plan_editor: 'Plan Editor',
|
||||||
plan_name_ph: 'E.g. Leg Day',
|
plan_name_ph: 'E.g. Full-body Routine',
|
||||||
plan_desc_ph: 'Describe preparation...',
|
plan_desc_ph: 'Describe preparation...',
|
||||||
exercises_list: 'Exercises',
|
exercises_list: 'Exercises',
|
||||||
weighted: 'Weighted',
|
weighted: 'Weighted',
|
||||||
@@ -263,7 +264,7 @@ const translations = {
|
|||||||
// Plans
|
// Plans
|
||||||
plans_empty: 'Нет созданных планов',
|
plans_empty: 'Нет созданных планов',
|
||||||
plan_editor: 'Редактор плана',
|
plan_editor: 'Редактор плана',
|
||||||
plan_name_ph: 'Например: День ног',
|
plan_name_ph: 'Например: Комплекс на всё тело',
|
||||||
plan_desc_ph: 'Опишите подготовку...',
|
plan_desc_ph: 'Опишите подготовку...',
|
||||||
exercises_list: 'Упражнения',
|
exercises_list: 'Упражнения',
|
||||||
weighted: 'С отягощением',
|
weighted: 'С отягощением',
|
||||||
|
|||||||
Reference in New Issue
Block a user