modified how sets are intereted in the frontend, they still get inserted the same way in the back so no DB changes needed
This commit is contained in:
@@ -16,9 +16,9 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||||
|
|
||||||
interface SetForm {
|
interface ExerciseRow {
|
||||||
exercise_id: number;
|
exercise_id: number;
|
||||||
set_number: number;
|
sets: string;
|
||||||
reps: string;
|
reps: string;
|
||||||
weight_lbs: string;
|
weight_lbs: string;
|
||||||
duration_min: string;
|
duration_min: string;
|
||||||
@@ -30,7 +30,7 @@ export default function Workouts() {
|
|||||||
const [date, setDate] = useState(today());
|
const [date, setDate] = useState(today());
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [wNotes, setWNotes] = useState("");
|
const [wNotes, setWNotes] = useState("");
|
||||||
const [sets, setSets] = useState<SetForm[]>([]);
|
const [rows, setRows] = useState<ExerciseRow[]>([]);
|
||||||
const [newExName, setNewExName] = useState("");
|
const [newExName, setNewExName] = useState("");
|
||||||
const [newExCategory, setNewExCategory] = useState("");
|
const [newExCategory, setNewExCategory] = useState("");
|
||||||
const [showNewExercise, setShowNewExercise] = useState(false);
|
const [showNewExercise, setShowNewExercise] = useState(false);
|
||||||
@@ -44,12 +44,12 @@ export default function Workouts() {
|
|||||||
load();
|
load();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addSet = () => {
|
const addRow = () => {
|
||||||
setSets([
|
setRows([
|
||||||
...sets,
|
...rows,
|
||||||
{
|
{
|
||||||
exercise_id: exercises[0]?.id || 0,
|
exercise_id: exercises[0]?.id || 0,
|
||||||
set_number: sets.length + 1,
|
sets: "3",
|
||||||
reps: "",
|
reps: "",
|
||||||
weight_lbs: "",
|
weight_lbs: "",
|
||||||
duration_min: "",
|
duration_min: "",
|
||||||
@@ -57,34 +57,42 @@ export default function Workouts() {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSet = (i: number, field: keyof SetForm, value: string | number) => {
|
const updateRow = (i: number, field: keyof ExerciseRow, value: string | number) => {
|
||||||
const next = [...sets];
|
const next = [...rows];
|
||||||
(next[i] as any)[field] = value;
|
(next[i] as any)[field] = value;
|
||||||
next[i].set_number = i + 1;
|
setRows(next);
|
||||||
setSets(next);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeSet = (i: number) => {
|
const removeRow = (i: number) => {
|
||||||
setSets(sets.filter((_, idx) => idx !== i).map((s, idx) => ({ ...s, set_number: idx + 1 })));
|
setRows(rows.filter((_, idx) => idx !== i));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
// Expand each row into individual sets
|
||||||
|
const allSets: { exercise_id: number; set_number: number; reps: number | null; weight_lbs: number | null; duration_min: number | null }[] = [];
|
||||||
|
let setNum = 1;
|
||||||
|
for (const row of rows) {
|
||||||
|
const numSets = parseInt(row.sets) || 1;
|
||||||
|
for (let s = 0; s < numSets; s++) {
|
||||||
|
allSets.push({
|
||||||
|
exercise_id: row.exercise_id,
|
||||||
|
set_number: setNum++,
|
||||||
|
reps: row.reps ? parseInt(row.reps) : null,
|
||||||
|
weight_lbs: row.weight_lbs ? parseFloat(row.weight_lbs) : null,
|
||||||
|
duration_min: row.duration_min ? parseFloat(row.duration_min) : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
await api.post("/workouts", {
|
await api.post("/workouts", {
|
||||||
date,
|
date,
|
||||||
name: name || null,
|
name: name || null,
|
||||||
notes: wNotes || null,
|
notes: wNotes || null,
|
||||||
sets: sets.map((s) => ({
|
sets: allSets,
|
||||||
exercise_id: s.exercise_id,
|
|
||||||
set_number: s.set_number,
|
|
||||||
reps: s.reps ? parseInt(s.reps) : null,
|
|
||||||
weight_lbs: s.weight_lbs ? parseFloat(s.weight_lbs) : null,
|
|
||||||
duration_min: s.duration_min ? parseFloat(s.duration_min) : null,
|
|
||||||
})),
|
|
||||||
});
|
});
|
||||||
setName("");
|
setName("");
|
||||||
setWNotes("");
|
setWNotes("");
|
||||||
setSets([]);
|
setRows([]);
|
||||||
load();
|
load();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,30 +140,44 @@ export default function Workouts() {
|
|||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label>Sets</Label>
|
<Label>Exercises</Label>
|
||||||
<Button type="button" variant="ghost" size="sm" onClick={addSet} disabled={exercises.length === 0}>
|
<Button type="button" variant="ghost" size="sm" onClick={addRow} disabled={exercises.length === 0}>
|
||||||
<Plus className="h-4 w-4 mr-1" /> Add Set
|
<Plus className="h-4 w-4 mr-1" /> Add Exercise
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{exercises.length === 0 && (
|
{exercises.length === 0 && (
|
||||||
<p className="text-xs text-muted-foreground">Create an exercise first</p>
|
<p className="text-xs text-muted-foreground">Create an exercise first</p>
|
||||||
)}
|
)}
|
||||||
{sets.map((s, i) => (
|
{rows.map((r, i) => (
|
||||||
<div key={i} className="flex gap-2 items-end">
|
<div key={i} className="space-y-2 border rounded-md p-2">
|
||||||
<select
|
<div className="flex gap-2 items-center">
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-2 text-sm"
|
<select
|
||||||
value={s.exercise_id}
|
className="flex h-9 w-full rounded-md border border-input bg-transparent px-2 text-sm"
|
||||||
onChange={(e) => updateSet(i, "exercise_id", parseInt(e.target.value))}
|
value={r.exercise_id}
|
||||||
>
|
onChange={(e) => updateRow(i, "exercise_id", parseInt(e.target.value))}
|
||||||
{exercises.map((ex) => (
|
>
|
||||||
<option key={ex.id} value={ex.id}>{ex.name}</option>
|
{exercises.map((ex) => (
|
||||||
))}
|
<option key={ex.id} value={ex.id}>{ex.name}</option>
|
||||||
</select>
|
))}
|
||||||
<Input className="w-16" placeholder="Reps" value={s.reps} onChange={(e) => updateSet(i, "reps", e.target.value)} />
|
</select>
|
||||||
<Input className="w-20" placeholder="Wt (lbs)" value={s.weight_lbs} onChange={(e) => updateSet(i, "weight_lbs", e.target.value)} />
|
<Button type="button" variant="ghost" size="icon" onClick={() => removeRow(i)}>
|
||||||
<Button type="button" variant="ghost" size="icon" onClick={() => removeSet(i)}>
|
<Trash2 className="h-3 w-3" />
|
||||||
<Trash2 className="h-3 w-3" />
|
</Button>
|
||||||
</Button>
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="flex-1">
|
||||||
|
<Label className="text-xs text-muted-foreground">Sets</Label>
|
||||||
|
<Input type="number" min="1" value={r.sets} onChange={(e) => updateRow(i, "sets", e.target.value)} />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<Label className="text-xs text-muted-foreground">Reps</Label>
|
||||||
|
<Input type="number" value={r.reps} onChange={(e) => updateRow(i, "reps", e.target.value)} />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<Label className="text-xs text-muted-foreground">Weight</Label>
|
||||||
|
<Input type="number" step="0.1" value={r.weight_lbs} onChange={(e) => updateRow(i, "weight_lbs", e.target.value)} placeholder="lbs" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user