-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dceec4f
commit 7d51e67
Showing
13 changed files
with
776 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
"use client"; | ||
|
||
import { BarChart, Card, Subtitle, Title } from "@tremor/react"; | ||
import { | ||
addMinutes, | ||
closestIndexTo, | ||
format, | ||
parseISO, | ||
startOfDay, | ||
} from "date-fns"; | ||
|
||
// Define a type for meal data | ||
interface MealData { | ||
time: string; // ISO string for meal time | ||
Fat: number; | ||
Carbs: number; | ||
Protein: number; | ||
} | ||
|
||
// Meal data with specific ISO times | ||
const sampleMeals: MealData[] = [ | ||
{ time: "2023-11-09T07:01:00Z", Fat: 300, Carbs: 200, Protein: 300 }, // Breakfast | ||
{ time: "2023-11-09T011:31:00Z", Fat: 100, Carbs: 500, Protein: 200 }, // Lunch | ||
{ time: "2023-11-09T14:01:00Z", Fat: 20, Carbs: 35, Protein: 25 }, // Snack | ||
{ time: "2023-11-09T18:01:00Z", Fat: 400, Carbs: 300, Protein: 500 }, // Dinner | ||
// ... other meals | ||
]; | ||
|
||
// Generate fixed times for every 30 minutes in a day | ||
const generateFixedTimes = (baseDate: Date): Date[] => { | ||
const startTime: Date = startOfDay(baseDate); // Start at midnight of the current day | ||
const fixedTimes: Date[] = []; | ||
for (let i = 0; i < 48; i++) { | ||
// 24 hours * 2 slots per hour | ||
fixedTimes.push(addMinutes(startTime, i * 30)); | ||
} | ||
return fixedTimes; | ||
}; | ||
|
||
// Function to round time to the nearest 30-minute window | ||
const getNearestHalfHour = (date: Date): string => { | ||
const minutes = date.getMinutes(); | ||
const roundedMinutes = minutes >= 30 ? 30 : 0; | ||
const roundedDate = new Date(date); | ||
roundedDate.setMinutes(roundedMinutes, 0, 0); // Set seconds and milliseconds to 0 for consistency | ||
|
||
return format(roundedDate, "HH:mm"); | ||
}; | ||
|
||
const date: Date = new Date(); // Your sample date | ||
const fixedHalfHours: Date[] = generateFixedTimes(date); | ||
|
||
// Convert fixed half-hours to strings for chartData | ||
const fixedTimeStrings: string[] = fixedHalfHours.map((time) => | ||
format(time, "HH:mm"), | ||
); | ||
|
||
// Initialize chart data with times as strings and zeroed macronutrients | ||
const chartData: MealData[] = fixedTimeStrings.map((time) => ({ | ||
time: time, // Use the string representation of time | ||
Fat: 0, | ||
Carbs: 0, | ||
Protein: 0, | ||
})); | ||
|
||
sampleMeals.forEach((meal) => { | ||
const mealTime: Date = parseISO(meal.time); | ||
const roundedTime: string = getNearestHalfHour(mealTime, fixedHalfHours); // Pass the array of fixed Date objects | ||
const dataIndex: number = chartData.findIndex( | ||
(data) => data.time === roundedTime, | ||
); | ||
|
||
if (dataIndex !== -1) { | ||
chartData[dataIndex].Fat += meal.Fat; | ||
chartData[dataIndex].Carbs += meal.Carbs; | ||
chartData[dataIndex].Protein += meal.Protein; | ||
} else { | ||
// If there is no matching time slot, log an error or handle appropriately | ||
console.error( | ||
`Meal time ${roundedTime} does not match any fixed time slot.`, | ||
); | ||
} | ||
}); | ||
|
||
const valueFormatter = (value) => `${value}`; | ||
|
||
const MacrosBar = () => ( | ||
<Card className="px-1"> | ||
<Title>Macronutrient Ratio per Meal</Title> | ||
<Subtitle>Daily intake ratios for fat, carbs, and protein.</Subtitle> | ||
<BarChart | ||
className="mt-6" | ||
data={chartData} | ||
index="time" | ||
categories={["Fat", "Carbs", "Protein"]} | ||
colors={["amber", "violet", "lime"]} // Gold for Fat, Turquoise for Carbs, YellowGreen for Protein | ||
stack={true} | ||
valueFormatter={valueFormatter} | ||
yAxisWidth={48} | ||
intervalType={"preserveStartEnd"} // This ensures that the first and last ticks are shown | ||
showXAxis={true} | ||
showYAxis={true} | ||
showAnimation={true} | ||
// Additional properties like animationDuration, showTooltip, etc., can be set as required. | ||
/> | ||
</Card> | ||
); | ||
|
||
export { MacrosBar }; |
90 changes: 90 additions & 0 deletions
90
apps/nextjs/src/components/dashboard/macros-donut-cods.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
"use client"; | ||
|
||
import React, { useEffect, useRef } from "react"; | ||
|
||
const CircularProgressBar = ({ macros }) => { | ||
const svgRef = useRef(null); | ||
const totalCalories = macros.reduce((acc, macro) => acc + macro.calories, 0); | ||
|
||
useEffect(() => { | ||
const progressElements = svgRef.current.querySelectorAll(".progress"); | ||
let cumulativePercent = 0; | ||
|
||
progressElements.forEach((progress, index) => { | ||
const percentage = (macros[index].calories / totalCalories) * 100; | ||
const radius = progress.r.baseVal.value; | ||
const circumference = 2 * Math.PI * radius; | ||
|
||
// Calculate the stroke dash offset for this segment | ||
const offset = circumference - (circumference * percentage) / 100; | ||
|
||
// The starting point for the next segment | ||
cumulativePercent += percentage; | ||
|
||
// Set the initial styles for the stroke dash array and offset | ||
progress.style.strokeDasharray = `${circumference}`; | ||
progress.style.strokeDashoffset = `${circumference}`; | ||
|
||
// Set the transition property | ||
progress.style.transition = `stroke-dashoffset ${macros[index].animateTime}s ease-in forwards`; | ||
|
||
// Set the timeout to trigger the animation | ||
setTimeout(() => { | ||
progress.style.strokeDashoffset = `${offset}`; | ||
}, 0); | ||
}); | ||
}, [macros, totalCalories]); | ||
|
||
return ( | ||
<div className="relative"> | ||
<svg | ||
ref={svgRef} | ||
className="h-80 w-80" | ||
viewBox="0 0 200 200" | ||
style={{ transform: "rotate(-90deg)" }} | ||
> | ||
{macros.map((macro, index) => ( | ||
<circle | ||
key={macro.name} | ||
className="progress" | ||
cx="100" | ||
cy="100" | ||
r="70" | ||
fill="none" | ||
stroke={macro.color} | ||
strokeWidth="15" | ||
// Inline styles are set in the useEffect hook | ||
/> | ||
))} | ||
</svg> | ||
</div> | ||
); | ||
}; | ||
|
||
const MacrosComponent = () => { | ||
const macros = [ | ||
{ | ||
name: "Fat", | ||
calories: 500, | ||
color: "#F59E0B", // amber | ||
animateTime: 1, // Animation time in seconds | ||
}, | ||
{ | ||
name: "Carbs", | ||
calories: 500, | ||
color: "#7C3AED", // violet | ||
animateTime: 1, | ||
}, | ||
{ | ||
name: "Protein", | ||
calories: 1000, | ||
color: "#10B981", // lime | ||
animateTime: 1, | ||
}, | ||
// You can add "Remaining" if you want to show it as well | ||
]; | ||
|
||
return <CircularProgressBar macros={macros} />; | ||
}; | ||
|
||
export { MacrosComponent }; |
Oops, something went wrong.