Changeset 708af9e
- Timestamp:
- 04/27/26 22:01:12 (2 months ago)
- Branches:
- master
- Children:
- 8449b9c
- Parents:
- 89156c1
- Files:
-
- 20 added
- 5 edited
-
backend/src/main/java/com/trekr/backend/controller/WeightController.java (added)
-
backend/src/main/java/com/trekr/backend/dto/weight/CreateDailyIntakeRequest.java (added)
-
backend/src/main/java/com/trekr/backend/dto/weight/TodayTrainingInfoDto.java (added)
-
backend/src/main/java/com/trekr/backend/dto/weight/TrackingStatusResponse.java (added)
-
backend/src/main/java/com/trekr/backend/dto/weight/WeightDailyIntakeDto.java (added)
-
backend/src/main/java/com/trekr/backend/dto/weight/WeightDailyIntakesResponse.java (added)
-
backend/src/main/java/com/trekr/backend/dto/weight/WeightProfileResponse.java (added)
-
backend/src/main/java/com/trekr/backend/dto/weight/WeightStartRequest.java (added)
-
backend/src/main/java/com/trekr/backend/entity/weight/WeightUser.java (modified) (1 diff)
-
backend/src/main/java/com/trekr/backend/repository/DailyIntakeRepository.java (added)
-
backend/src/main/java/com/trekr/backend/repository/TrainingSessionRepository.java (modified) (1 diff)
-
backend/src/main/java/com/trekr/backend/repository/WeightUserRepository.java (added)
-
backend/src/main/java/com/trekr/backend/service/WeightService.java (added)
-
db-scripts/ddl.sql (modified) (2 diffs)
-
frontend/src/App.jsx (modified) (2 diffs)
-
frontend/src/pages/Dashboard/pages/Weight/NewWeightIntake.jsx (added)
-
frontend/src/pages/Dashboard/pages/Weight/Weight.jsx (modified) (1 diff)
-
frontend/src/pages/Dashboard/pages/Weight/WeightTracking.jsx (added)
-
frontend/src/pages/Dashboard/pages/Weight/components/WeightCenteredCard.jsx (added)
-
frontend/src/pages/Dashboard/pages/Weight/components/WeightIntakesTable.jsx (added)
-
frontend/src/pages/Dashboard/pages/Weight/components/WeightProgressCard.jsx (added)
-
frontend/src/pages/Dashboard/pages/Weight/components/WeightStartCtaCard.jsx (added)
-
frontend/src/pages/Dashboard/pages/Weight/components/WeightStartForm.jsx (added)
-
frontend/src/pages/Dashboard/pages/Weight/components/WeightSummaryCard.jsx (added)
-
frontend/src/pages/Dashboard/pages/Weight/components/WeightTrackingHeader.jsx (added)
Legend:
- Unmodified
- Added
- Removed
-
backend/src/main/java/com/trekr/backend/entity/weight/WeightUser.java
r89156c1 r708af9e 81 81 } 82 82 83 83 84 public BigDecimal getGoalCalories() { 84 85 return goalCalories; -
backend/src/main/java/com/trekr/backend/repository/TrainingSessionRepository.java
r89156c1 r708af9e 10 10 public interface TrainingSessionRepository extends JpaRepository<TrainingSession, Long> { 11 11 Page<TrainingSession> findByTrainingUser_UserIdOrderByDateDesc(Long userId, Pageable pageable); 12 java.util.List<TrainingSession> findByTrainingUser_UserIdAndDate(Long userId, java.time.LocalDate date); 12 13 } -
db-scripts/ddl.sql
r89156c1 r708af9e 55 55 user_id BIGINT PRIMARY KEY 56 56 REFERENCES USERS(user_id) ON DELETE CASCADE, 57 weight NUMERIC ,58 height NUMERIC ,59 goal_weight NUMERIC ,60 goal_calories NUMERIC 57 weight NUMERIC NOT NULL, 58 height NUMERIC NOT NULL, 59 goal_weight NUMERIC NOT NULL, 60 goal_calories NUMERIC NOT NULL 61 61 ); 62 62 … … 65 65 user_id BIGINT 66 66 REFERENCES WEIGHT_USERS(user_id) ON DELETE CASCADE, 67 calories NUMERIC, 68 date DATE 67 calories NUMERIC NOT NULL, 68 date DATE NOT NULL, 69 70 CONSTRAINT daily_intakes_unique_user_date UNIQUE (user_id, date) 69 71 ); 70 72 -
frontend/src/App.jsx
r89156c1 r708af9e 10 10 import NewTrainingSession from "./pages/Dashboard/pages/Training/NewTrainingSession.jsx"; 11 11 import Weight from "./pages/Dashboard/pages/Weight/Weight.jsx"; 12 import WeightTracking from "./pages/Dashboard/pages/Weight/WeightTracking.jsx"; 13 import NewWeightIntake from "./pages/Dashboard/pages/Weight/NewWeightIntake.jsx"; 12 14 import Finance from "./pages/Dashboard/pages/Finance/Finance.jsx"; 13 15 import Investing from "./pages/Dashboard/pages/Investing/Investing.jsx"; … … 46 48 /> 47 49 <Route path="weight" element={<Weight />} /> 50 <Route path="weight/tracking" element={<WeightTracking />} /> 51 <Route path="weight/intakes/new" element={<NewWeightIntake />} /> 48 52 <Route path="finance" element={<Finance />} /> 49 53 <Route path="investing" element={<Investing />} /> -
frontend/src/pages/Dashboard/pages/Weight/Weight.jsx
r89156c1 r708af9e 1 import React from "react"; 1 import React, { useEffect, useMemo, useState } from "react"; 2 import { useNavigate } from "react-router-dom"; 3 4 import api from "../../../../api/axios"; 5 6 import WeightCenteredCard from "./components/WeightCenteredCard.jsx"; 7 import WeightStartCtaCard from "./components/WeightStartCtaCard.jsx"; 8 import WeightStartForm from "./components/WeightStartForm.jsx"; 9 10 function estimateGoalTimeWeeks(currentWeight, goalWeight) { 11 const current = Number(currentWeight); 12 const goal = Number(goalWeight); 13 if (!Number.isFinite(current) || !Number.isFinite(goal) || current <= 0 || goal <= 0) { 14 return null; 15 } 16 const difference = Math.abs(goal - current); 17 return Math.round((difference / 0.5) * 100) / 100; 18 } 19 20 function estimateGoalCalories(currentWeight, height, goalWeight) { 21 const current = Number(currentWeight); 22 const h = Number(height); 23 const goal = Number(goalWeight); 24 if (!Number.isFinite(current) || !Number.isFinite(h) || !Number.isFinite(goal)) { 25 return null; 26 } 27 28 const maintenance = current * 10 + h * 6.25 + 50; 29 if (goal < current) return Math.max(0, Math.round((maintenance - 500) * 100) / 100); 30 if (goal > current) return Math.round((maintenance + 300) * 100) / 100; 31 return Math.round(maintenance * 100) / 100; 32 } 2 33 3 34 const Weight = () => { 35 const navigate = useNavigate(); 36 37 const [isLoading, setIsLoading] = useState(true); 38 const [isTracking, setIsTracking] = useState(false); 39 const [step, setStep] = useState("cta"); 40 const [error, setError] = useState(""); 41 const [isSubmitting, setIsSubmitting] = useState(false); 42 const [autoCalculateTargets, setAutoCalculateTargets] = useState(true); 43 44 const initialForm = useMemo( 45 () => ({ 46 weight: "", 47 height: "", 48 goalWeight: "", 49 goalTimeWeeks: "", 50 goalCalories: "", 51 }), 52 [], 53 ); 54 const [form, setForm] = useState(initialForm); 55 56 const estimatedGoalTimeWeeks = useMemo( 57 () => estimateGoalTimeWeeks(form.weight, form.goalWeight), 58 [form.weight, form.goalWeight], 59 ); 60 const estimatedGoalCalories = useMemo( 61 () => estimateGoalCalories(form.weight, form.height, form.goalWeight), 62 [form.weight, form.height, form.goalWeight], 63 ); 64 65 useEffect(() => { 66 let isMounted = true; 67 const run = async () => { 68 setIsLoading(true); 69 setError(""); 70 try { 71 const res = await api.get("/weight/status"); 72 const tracking = Boolean(res?.data?.tracking); 73 if (!isMounted) return; 74 setIsTracking(tracking); 75 if (tracking) { 76 navigate("/dashboard/weight/tracking", { replace: true }); 77 } 78 } catch (err) { 79 if (!isMounted) return; 80 const message = err?.response?.data?.message || "Failed to load weight status"; 81 setError(message); 82 } finally { 83 if (isMounted) setIsLoading(false); 84 } 85 }; 86 87 run(); 88 return () => { 89 isMounted = false; 90 }; 91 }, [navigate]); 92 93 const onStart = () => { 94 setError(""); 95 setStep("form"); 96 }; 97 98 const onCancel = () => { 99 setStep("cta"); 100 setForm(initialForm); 101 setAutoCalculateTargets(true); 102 setError(""); 103 }; 104 105 const onSubmit = async (e) => { 106 e.preventDefault(); 107 setError(""); 108 setIsSubmitting(true); 109 110 try { 111 await api.post("/weight/start", { 112 weight: form.weight === "" ? null : Number(form.weight), 113 height: form.height === "" ? null : Number(form.height), 114 goalWeight: form.goalWeight === "" ? null : Number(form.goalWeight), 115 goalCalories: autoCalculateTargets 116 ? null 117 : form.goalCalories === "" 118 ? null 119 : Number(form.goalCalories), 120 autoCalculateTargets, 121 }); 122 123 setIsTracking(true); 124 navigate("/dashboard/weight/tracking", { replace: true }); 125 } catch (err) { 126 const message = 127 err?.response?.data?.message || 128 Object.values(err?.response?.data?.errors ?? {})[0] || 129 "Failed to start weight tracking"; 130 setError(message); 131 } finally { 132 setIsSubmitting(false); 133 } 134 }; 135 136 if (isLoading) { 137 return <WeightCenteredCard title="Weight" message="Loading…" />; 138 } 139 140 if (isTracking) { 141 return <WeightCenteredCard title="Weight" message="Redirecting…" />; 142 } 143 4 144 return ( 5 <div className="space-y-4"> 6 <h1 className="text-2xl font-bold">Weight</h1> 7 <div className="card bg-base-200 border border-base-300"> 8 <div className="card-body"> 9 <p className="opacity-80">Weight dashboard content goes here.</p> 10 </div> 145 <div className="min-h-[70vh] w-full flex items-center justify-center"> 146 <div className="w-full max-w-4xl"> 147 {step === "cta" ? ( 148 <WeightStartCtaCard error={error} onStart={onStart} isSubmitting={isSubmitting} /> 149 ) : ( 150 <WeightStartForm 151 form={form} 152 setForm={setForm} 153 onSubmit={onSubmit} 154 onCancel={onCancel} 155 error={error} 156 isSubmitting={isSubmitting} 157 autoCalculateTargets={autoCalculateTargets} 158 setAutoCalculateTargets={setAutoCalculateTargets} 159 estimatedGoalTimeWeeks={estimatedGoalTimeWeeks} 160 estimatedGoalCalories={estimatedGoalCalories} 161 /> 162 )} 11 163 </div> 12 164 </div>
Note:
See TracChangeset
for help on using the changeset viewer.
