110 lines
3.4 KiB
JavaScript
110 lines
3.4 KiB
JavaScript
/**
|
|
* Chart data transformation utilities for baby sleep tracker
|
|
*/
|
|
/**
|
|
* Build timetable slots for visualization
|
|
* @param slots - raw timetable slots from API
|
|
* @returns grouped slots by date
|
|
*/
|
|
export function buildTimetableSlots(slots) {
|
|
return slots.reduce((acc, slot) => {
|
|
if (!acc[slot.date]) {
|
|
acc[slot.date] = [];
|
|
}
|
|
acc[slot.date].push(slot);
|
|
return acc;
|
|
}, {});
|
|
}
|
|
/**
|
|
* Transform wakeup minutes for Recharts LineChart
|
|
* @param days - wakeup/bedtime data from API
|
|
* @returns array formatted for LineChart
|
|
*/
|
|
export function computeWakeupMinutes(days, wakeupRef = "04:00") {
|
|
return days.map((day) => ({
|
|
date: day.date,
|
|
minutes: day.wakeup_minutes ?? 0,
|
|
time: day.wakeup_minutes !== null
|
|
? formatMinutesAfter(day.wakeup_minutes, wakeupRef)
|
|
: "N/A",
|
|
}));
|
|
}
|
|
/**
|
|
* Transform bedtime minutes for Recharts LineChart
|
|
* @param days - wakeup/bedtime data from API
|
|
* @returns array formatted for LineChart
|
|
*/
|
|
export function computeBedtimeMinutes(days, bedtimeRef = "19:00") {
|
|
return days.map((day) => ({
|
|
date: day.date,
|
|
minutes: day.bedtime_minutes ?? 0,
|
|
time: day.bedtime_minutes !== null
|
|
? formatMinutesAfter(day.bedtime_minutes, bedtimeRef)
|
|
: "N/A",
|
|
}));
|
|
}
|
|
/**
|
|
* Transform daily summary for Recharts BarChart (vertical layout)
|
|
* @param days - daily summary data from API
|
|
* @returns array formatted for BarChart
|
|
*/
|
|
export function computeDailySummaryData(days) {
|
|
return days.map((day) => ({
|
|
date: day.date,
|
|
daySleep: day.day_minutes,
|
|
nightSleep: day.night_minutes,
|
|
total: day.total_minutes,
|
|
}));
|
|
}
|
|
/**
|
|
* Calculate average summary stats from daily data
|
|
* @param days - daily summary data from API
|
|
* @returns averages for total, day, and night sleep
|
|
*/
|
|
export function computeAverageStats(days) {
|
|
if (!days.length) {
|
|
return { avgTotal: 0, avgDay: 0, avgNight: 0, count: 0 };
|
|
}
|
|
const totals = days.reduce((acc, day) => ({
|
|
total: acc.total + day.total_minutes,
|
|
day: acc.day + day.day_minutes,
|
|
night: acc.night + day.night_minutes,
|
|
}), { total: 0, day: 0, night: 0 });
|
|
const count = days.length;
|
|
return {
|
|
avgTotal: Math.round(totals.total / count),
|
|
avgDay: Math.round(totals.day / count),
|
|
avgNight: Math.round(totals.night / count),
|
|
count,
|
|
};
|
|
}
|
|
/**
|
|
* Convert minutes after reference to clock time string
|
|
* Internal helper - kept for chartHelpers module
|
|
*/
|
|
function formatMinutesAfter(minutes, reference) {
|
|
const [refHours, refMinutes] = reference.split(":").map(Number);
|
|
const totalRefMinutes = refHours * 60 + refMinutes;
|
|
const targetMinutes = totalRefMinutes + minutes;
|
|
const normalizedMinutes = ((targetMinutes % 1440) + 1440) % 1440;
|
|
const hours = Math.floor(normalizedMinutes / 60);
|
|
const mins = normalizedMinutes % 60;
|
|
return `${String(hours).padStart(2, "0")}:${String(mins).padStart(2, "0")}`;
|
|
}
|
|
/**
|
|
* Reverse date order (newest first for charts)
|
|
* @param days - array of day data
|
|
* @returns reversed array
|
|
*/
|
|
export function reverseDateOrder(days) {
|
|
return [...days].reverse();
|
|
}
|
|
/**
|
|
* Filter days to only include those with data
|
|
* @param days - array of day data
|
|
* @returns filtered array
|
|
*/
|
|
export function filterDaysWithData(days) {
|
|
return days.filter((day) => (day.total_minutes ?? 0) > 0);
|
|
}
|