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:
YOUNG
2026-03-10 09:17:09 -05:00
parent 4802d9a6d3
commit 3504093c33

View File

@@ -16,9 +16,9 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
interface SetForm {
interface ExerciseRow {
exercise_id: number;
set_number: number;
sets: string;
reps: string;
weight_lbs: string;
duration_min: string;
@@ -30,7 +30,7 @@ export default function Workouts() {
const [date, setDate] = useState(today());
const [name, setName] = useState("");
const [wNotes, setWNotes] = useState("");
const [sets, setSets] = useState<SetForm[]>([]);
const [rows, setRows] = useState<ExerciseRow[]>([]);
const [newExName, setNewExName] = useState("");
const [newExCategory, setNewExCategory] = useState("");
const [showNewExercise, setShowNewExercise] = useState(false);
@@ -44,12 +44,12 @@ export default function Workouts() {
load();
}, []);
const addSet = () => {
setSets([
...sets,
const addRow = () => {
setRows([
...rows,
{
exercise_id: exercises[0]?.id || 0,
set_number: sets.length + 1,
sets: "3",
reps: "",
weight_lbs: "",
duration_min: "",
@@ -57,34 +57,42 @@ export default function Workouts() {
]);
};
const updateSet = (i: number, field: keyof SetForm, value: string | number) => {
const next = [...sets];
const updateRow = (i: number, field: keyof ExerciseRow, value: string | number) => {
const next = [...rows];
(next[i] as any)[field] = value;
next[i].set_number = i + 1;
setSets(next);
setRows(next);
};
const removeSet = (i: number) => {
setSets(sets.filter((_, idx) => idx !== i).map((s, idx) => ({ ...s, set_number: idx + 1 })));
const removeRow = (i: number) => {
setRows(rows.filter((_, idx) => idx !== i));
};
const handleSubmit = async (e: React.FormEvent) => {
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", {
date,
name: name || null,
notes: wNotes || null,
sets: sets.map((s) => ({
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,
})),
sets: allSets,
});
setName("");
setWNotes("");
setSets([]);
setRows([]);
load();
};
@@ -132,30 +140,44 @@ export default function Workouts() {
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label>Sets</Label>
<Button type="button" variant="ghost" size="sm" onClick={addSet} disabled={exercises.length === 0}>
<Plus className="h-4 w-4 mr-1" /> Add Set
<Label>Exercises</Label>
<Button type="button" variant="ghost" size="sm" onClick={addRow} disabled={exercises.length === 0}>
<Plus className="h-4 w-4 mr-1" /> Add Exercise
</Button>
</div>
{exercises.length === 0 && (
<p className="text-xs text-muted-foreground">Create an exercise first</p>
)}
{sets.map((s, i) => (
<div key={i} className="flex gap-2 items-end">
<select
className="flex h-9 w-full rounded-md border border-input bg-transparent px-2 text-sm"
value={s.exercise_id}
onChange={(e) => updateSet(i, "exercise_id", parseInt(e.target.value))}
>
{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)} />
<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={() => removeSet(i)}>
<Trash2 className="h-3 w-3" />
</Button>
{rows.map((r, i) => (
<div key={i} className="space-y-2 border rounded-md p-2">
<div className="flex gap-2 items-center">
<select
className="flex h-9 w-full rounded-md border border-input bg-transparent px-2 text-sm"
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>
))}
</select>
<Button type="button" variant="ghost" size="icon" onClick={() => removeRow(i)}>
<Trash2 className="h-3 w-3" />
</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>