Deploy 2026-06-15T16:19:01.745Z
This commit is contained in:
@@ -33,6 +33,13 @@
|
|||||||
|
|
||||||
/* Meals */
|
/* Meals */
|
||||||
.meals-panel { overflow-y: auto; }
|
.meals-panel { overflow-y: auto; }
|
||||||
|
|
||||||
|
/* Exercise */
|
||||||
|
.exercise-list { display: flex; flex-direction: column; gap: 0.4rem; }
|
||||||
|
.exercise-item { display: flex; justify-content: space-between; align-items: center; padding: 0.4rem 0; border-bottom: 1px solid #0a1628; font-size: 0.85rem; }
|
||||||
|
.exercise-name { color: #e0e0e0; }
|
||||||
|
.exercise-details { color: #7ec8e3; font-weight: 600; white-space: nowrap; }
|
||||||
|
.exercise-total { display: flex; justify-content: space-between; padding-top: 0.5rem; font-weight: 700; color: #4caf50; font-size: 0.85rem; }
|
||||||
.meal-group { margin-bottom: 0.5rem; }
|
.meal-group { margin-bottom: 0.5rem; }
|
||||||
.meal-item { display: grid; grid-template-columns: 160px 1fr auto; gap: 0.5rem; align-items: baseline; padding: 0.3rem 0; border-bottom: 1px solid #0a1628; font-size: 0.82rem; }
|
.meal-item { display: grid; grid-template-columns: 160px 1fr auto; gap: 0.5rem; align-items: baseline; padding: 0.3rem 0; border-bottom: 1px solid #0a1628; font-size: 0.82rem; }
|
||||||
.item-name { font-weight: 500; color: #e0e0e0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.item-name { font-weight: 500; color: #e0e0e0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ function today() { return new Date().toISOString().slice(0, 10) }
|
|||||||
export default function Dashboard({ onBack }) {
|
export default function Dashboard({ onBack }) {
|
||||||
const [date, setDate] = useState(today())
|
const [date, setDate] = useState(today())
|
||||||
const [meals, setMeals] = useState(null)
|
const [meals, setMeals] = useState(null)
|
||||||
|
const [exercise, setExercise] = useState(null)
|
||||||
const [health, setHealth] = useState(null)
|
const [health, setHealth] = useState(null)
|
||||||
const [suggestions, setSuggestions] = useState('')
|
const [suggestions, setSuggestions] = useState('')
|
||||||
const [loadingSuggestions, setLoadingSuggestions] = useState(false)
|
const [loadingSuggestions, setLoadingSuggestions] = useState(false)
|
||||||
@@ -22,8 +23,9 @@ export default function Dashboard({ onBack }) {
|
|||||||
useEffect(() => { fetchData() }, [date])
|
useEffect(() => { fetchData() }, [date])
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
const [mealRes, weightRes, bfRes, hrRes, hrvRes, stepsRes, energyRes] = await Promise.all([
|
const [mealRes, exRes, weightRes, bfRes, hrRes, hrvRes, stepsRes, energyRes] = await Promise.all([
|
||||||
fetch(`${API_URL}/fatsecret/food-entries?date=${date}`).then(r => r.json()).catch(() => ({ entries: [] })),
|
fetch(`${API_URL}/fatsecret/food-entries?date=${date}`).then(r => r.json()).catch(() => ({ entries: [] })),
|
||||||
|
fetch(`${API_URL}/fatsecret/exercise-entries?date=${date}`).then(r => r.json()).catch(() => ({ entries: [] })),
|
||||||
fetch(`${API_URL}/health-data?metric=weight_body_mass&from=${date}`).then(r => r.json()).catch(() => ({ data: [] })),
|
fetch(`${API_URL}/health-data?metric=weight_body_mass&from=${date}`).then(r => r.json()).catch(() => ({ data: [] })),
|
||||||
fetch(`${API_URL}/health-data?metric=body_fat_percentage&from=${date}`).then(r => r.json()).catch(() => ({ data: [] })),
|
fetch(`${API_URL}/health-data?metric=body_fat_percentage&from=${date}`).then(r => r.json()).catch(() => ({ data: [] })),
|
||||||
fetch(`${API_URL}/health-data?metric=resting_heart_rate&from=${date}`).then(r => r.json()).catch(() => ({ data: [] })),
|
fetch(`${API_URL}/health-data?metric=resting_heart_rate&from=${date}`).then(r => r.json()).catch(() => ({ data: [] })),
|
||||||
@@ -32,6 +34,7 @@ export default function Dashboard({ onBack }) {
|
|||||||
fetch(`${API_URL}/health-data?metric=active_energy&from=${date}`).then(r => r.json()).catch(() => ({ data: [] })),
|
fetch(`${API_URL}/health-data?metric=active_energy&from=${date}`).then(r => r.json()).catch(() => ({ data: [] })),
|
||||||
])
|
])
|
||||||
setMeals(mealRes.entries || [])
|
setMeals(mealRes.entries || [])
|
||||||
|
setExercise((exRes.entries || []).filter(e => !['Resting', 'Sleeping'].includes(e.exercise_name)))
|
||||||
const forDate = (res) => (res.data || []).filter(d => d.date?.startsWith(date))
|
const forDate = (res) => (res.data || []).filter(d => d.date?.startsWith(date))
|
||||||
const stepsForDay = forDate(stepsRes).filter(d => {
|
const stepsForDay = forDate(stepsRes).filter(d => {
|
||||||
const hour = parseInt(d.date?.slice(11, 13) || '0')
|
const hour = parseInt(d.date?.slice(11, 13) || '0')
|
||||||
@@ -149,6 +152,25 @@ export default function Dashboard({ onBack }) {
|
|||||||
) : <p className="loading">Loading...</p>}
|
) : <p className="loading">Loading...</p>}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Exercise Panel */}
|
||||||
|
{exercise && exercise.length > 0 && (
|
||||||
|
<section className="panel exercise-panel">
|
||||||
|
<h2>🏋️ Exercise</h2>
|
||||||
|
<div className="exercise-list">
|
||||||
|
{exercise.map((e, i) => (
|
||||||
|
<div key={i} className="exercise-item">
|
||||||
|
<span className="exercise-name">{e.exercise_name}</span>
|
||||||
|
<span className="exercise-details">{e.minutes} min · {e.calories} cal</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="exercise-total">
|
||||||
|
<span>Total</span>
|
||||||
|
<span>{exercise.reduce((s, e) => s + (+e.minutes || 0), 0)} min · {exercise.reduce((s, e) => s + (+e.calories || 0), 0)} cal</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Macros Panel */}
|
{/* Macros Panel */}
|
||||||
<section className="panel macros-panel">
|
<section className="panel macros-panel">
|
||||||
<h2>🎯 Macros</h2>
|
<h2>🎯 Macros</h2>
|
||||||
|
|||||||
Reference in New Issue
Block a user