diff --git a/src/baby_monitor/static/diapers.html b/src/baby_monitor/static/diapers.html index b02239c..7183d92 100644 --- a/src/baby_monitor/static/diapers.html +++ b/src/baby_monitor/static/diapers.html @@ -281,6 +281,9 @@ let currentDaysRange = parseInt(localStorage.getItem("lastDiaperTimeRange")) || 7; let selectedChildId = null; // null means show first/only child + let barChartInstance = null; // Track chart instance for proper cleanup + let poopPieChartInstance = null; + let peePieChartInstance = null; // Restore last selected child from localStorage const lastChildId = localStorage.getItem("lastDiaperChildFilter"); @@ -782,7 +785,12 @@ function renderBarChart(chartData) { const ctx = document.getElementById("diaperChart").getContext("2d"); - new Chart(ctx, { + // Destroy existing chart instance if it exists + if (barChartInstance) { + barChartInstance.destroy(); + } + + barChartInstance = new Chart(ctx, { type: "bar", data: { labels: chartData.labels, @@ -875,6 +883,11 @@ function renderPoopPieChart(chartData) { const ctx = document.getElementById("poopPieChart").getContext("2d"); + // Destroy existing chart instance if it exists + if (poopPieChartInstance) { + poopPieChartInstance.destroy(); + } + const colors = { Light: { bg: "rgba(139, 69, 19, 0.5)", @@ -901,7 +914,7 @@ (label) => colors[label]?.border || "rgba(200, 200, 200, 1)", ); - new Chart(ctx, { + poopPieChartInstance = new Chart(ctx, { type: "pie", data: { labels: chartData.labels, @@ -957,6 +970,11 @@ function renderPeePieChart(chartData) { const ctx = document.getElementById("peePieChart").getContext("2d"); + // Destroy existing chart instance if it exists + if (peePieChartInstance) { + peePieChartInstance.destroy(); + } + const colors = { Light: { bg: "rgba(255, 215, 0, 0.5)", @@ -983,7 +1001,7 @@ (label) => colors[label]?.border || "rgba(200, 200, 200, 1)", ); - new Chart(ctx, { + peePieChartInstance = new Chart(ctx, { type: "pie", data: { labels: chartData.labels, diff --git a/src/baby_monitor/static/feedings.html b/src/baby_monitor/static/feedings.html index 3336189..241b33a 100644 --- a/src/baby_monitor/static/feedings.html +++ b/src/baby_monitor/static/feedings.html @@ -319,6 +319,8 @@ parseInt(localStorage.getItem("lastFeedingTimeRange")) || 7; let selectedChildId = null; // null means show first/only child let barChartInstance = null; + let pieChartInstance = null; + let durationChartInstance = null; // Restore last selected child from localStorage const lastChildId = localStorage.getItem("lastFeedingChildFilter"); @@ -979,6 +981,11 @@ function renderPieChart(chartData) { const ctx = document.getElementById("pieChart").getContext("2d"); + // Destroy existing chart instance if it exists + if (pieChartInstance) { + pieChartInstance.destroy(); + } + // Color mapping for each type const colorMap = { "Left Breast": { @@ -1003,7 +1010,7 @@ (label) => colorMap[label]?.border || "rgba(200, 200, 200, 1)", ); - new Chart(ctx, { + pieChartInstance = new Chart(ctx, { type: "pie", data: { labels: chartData.labels, @@ -1059,7 +1066,12 @@ function renderDurationChart(chartData) { const ctx = document.getElementById("durationChart").getContext("2d"); - new Chart(ctx, { + // Destroy existing chart instance if it exists + if (durationChartInstance) { + durationChartInstance.destroy(); + } + + durationChartInstance = new Chart(ctx, { type: "bar", data: { labels: chartData.labels, diff --git a/src/baby_monitor/static/index.html b/src/baby_monitor/static/index.html index c382d67..153c7b2 100644 --- a/src/baby_monitor/static/index.html +++ b/src/baby_monitor/static/index.html @@ -167,6 +167,52 @@ margin-top: 0.5rem; opacity: 0.9; } + .recent-activity { + margin-top: 2rem; + padding: 1.5rem; + background: #f8f9fa; + border-radius: 8px; + } + .recent-activity h3 { + margin-top: 0; + margin-bottom: 1rem; + color: #333; + font-size: 1.1rem; + } + .activity-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + } + .activity-card { + background: white; + padding: 1rem; + border-radius: 6px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + } + .activity-card .icon { + font-size: 1.5rem; + margin-bottom: 0.5rem; + } + .activity-card .type { + font-weight: 600; + color: #333; + margin-bottom: 0.25rem; + } + .activity-card .time { + font-size: 0.85rem; + color: #666; + } + .activity-card .details { + font-size: 0.85rem; + color: #888; + margin-top: 0.5rem; + } + .activity-card.empty { + opacity: 0.5; + font-style: italic; + color: #999; + }
@@ -321,6 +367,7 @@ let activeSleep = null; let lastSleep = null; let sleepUpdateTimerInterval = null; + let lastDiaperChange = null; async function loadFeedingStatus(children) { userChildren = children; @@ -376,6 +423,20 @@ lastSleep = sleeps[0]; } } + + // Fetch all diaper changes to get the last one + const diaperResponse = await fetch("/api/diaper-changes", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (diaperResponse.ok) { + const diapers = await diaperResponse.json(); + if (diapers.length > 0) { + lastDiaperChange = diapers[0]; + } + } } catch (error) { console.error("Error loading feeding status:", error); } @@ -478,9 +539,98 @@ ${feedingButtonHtml} ${sleepButtonHtml} + ${buildRecentActivitySection()} `; } + function buildRecentActivitySection() { + // Build diaper card + const diaperCard = lastDiaperChange + ? ` +