2 Commits
Author SHA1 Message Date
Brian Bjarke Jensen 44c6f0168f added feedings page with graphs and list of entries with option to edit and manually add new
Build and Push Docker Image / build-and-push (pull_request) Successful in 58s
Python Code Quality / python-code-quality (pull_request) Successful in 10s
Python Test / python-test (pull_request) Successful in 17s
2025-11-09 21:23:09 +01:00
Brian Bjarke Jensen 8d5e2a99ef removed deprecated yaml field 2025-11-09 19:12:49 +01:00
5 changed files with 1148 additions and 18 deletions
-2
View File
@@ -37,8 +37,6 @@ open http://localhost:8000
### Using Docker Compose
```yaml
version: "3.8"
services:
baby-monitor:
image: gitea.gt-proj.com/brian/baby-monitor:latest
+6
View File
@@ -91,6 +91,12 @@ def serve_log_feeding() -> FileResponse:
return FileResponse(static_path / "log-feeding.html")
@app.get("/feedings.html", include_in_schema=False)
def serve_feedings() -> FileResponse:
"""Serve the feedings overview page."""
return FileResponse(static_path / "feedings.html")
@app.get("/api/")
def read_root(token: Annotated[str, Depends(verify_token)]) -> dict:
"""API root endpoint (requires authentication)."""
File diff suppressed because it is too large Load Diff
+5
View File
@@ -216,6 +216,7 @@
<div class="menu-overlay" id="menuOverlay">
<div class="menu-item" id="menuAddChild">👶 Add Child</div>
<div class="menu-item" id="menuFeedings">🍼 Feedings</div>
<div class="menu-item logout" id="menuLogout">🚪 Logout</div>
</div>
@@ -285,6 +286,10 @@
window.location.href = "/add-child.html";
});
document.getElementById("menuFeedings").addEventListener("click", () => {
window.location.href = "/feedings.html";
});
document.getElementById("menuLogout").addEventListener("click", () => {
closeMenu();
logout();
+72 -9
View File
@@ -212,7 +212,13 @@
const loadingDiv = document.getElementById("loadingChildren");
const childSelect = document.getElementById("childId");
// Set default start time to now
// Check if we're editing an existing feeding
const urlParams = new URLSearchParams(window.location.search);
const feedingId = urlParams.get("id");
const isEditMode = !!feedingId;
// Set default start time to now (only for new feedings)
if (!isEditMode) {
const now = new Date();
const localDateTime = new Date(
now.getTime() - now.getTimezoneOffset() * 60000,
@@ -220,6 +226,14 @@
.toISOString()
.slice(0, 16);
document.getElementById("startTime").value = localDateTime;
}
// Update title and button text if editing
if (isEditMode) {
document.querySelector("h1").textContent = "🍼 Edit Feeding";
document.querySelector(".subtitle").textContent = "Update feeding session details";
submitBtn.textContent = "Update Feeding";
}
// Load user's children
async function loadChildren() {
@@ -255,6 +269,11 @@
// Show form, hide loading
loadingDiv.style.display = "none";
form.style.display = "block";
// If editing, load the feeding data
if (isEditMode) {
loadFeedingData();
}
} else {
showMessage("Failed to load children", "error");
}
@@ -263,6 +282,44 @@
}
}
async function loadFeedingData() {
try {
const response = await fetch(`/api/feedings/${feedingId}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (response.ok) {
const feeding = await response.json();
// Populate form with existing data
document.getElementById("childId").value = feeding.child_id;
// Format datetime for datetime-local input
const startTime = new Date(feeding.start_time);
const startLocalDateTime = new Date(
startTime.getTime() - startTime.getTimezoneOffset() * 60000
).toISOString().slice(0, 16);
document.getElementById("startTime").value = startLocalDateTime;
if (feeding.end_time) {
const endTime = new Date(feeding.end_time);
const endLocalDateTime = new Date(
endTime.getTime() - endTime.getTimezoneOffset() * 60000
).toISOString().slice(0, 16);
document.getElementById("endTime").value = endLocalDateTime;
}
document.getElementById("feedingType").value = feeding.feeding_type;
} else {
showMessage("Failed to load feeding data", "error");
}
} catch (error) {
showMessage("Network error loading feeding", "error");
}
}
loadChildren();
form.addEventListener("submit", async (e) => {
@@ -275,12 +332,15 @@
// Disable button during request
submitBtn.disabled = true;
submitBtn.textContent = "Logging...";
submitBtn.textContent = isEditMode ? "Updating..." : "Logging...";
messageDiv.classList.remove("show");
try {
const response = await fetch("/api/feedings", {
method: "POST",
const url = isEditMode ? `/api/feedings/${feedingId}` : "/api/feedings";
const method = isEditMode ? "PUT" : "POST";
const response = await fetch(url, {
method: method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
@@ -296,14 +356,17 @@
const data = await response.json();
if (response.ok) {
showMessage("Feeding logged successfully!", "success");
// Redirect to home page after 1 second
showMessage(
isEditMode ? "Feeding updated successfully!" : "Feeding logged successfully!",
"success"
);
// Redirect to feedings page after 1 second
setTimeout(() => {
window.location.href = "/";
window.location.href = "/feedings.html";
}, 1000);
} else {
showMessage(
data.detail || "Failed to log feeding. Please try again.",
data.detail || `Failed to ${isEditMode ? 'update' : 'log'} feeding. Please try again.`,
"error",
);
}
@@ -311,7 +374,7 @@
showMessage("Network error. Please try again.", "error");
} finally {
submitBtn.disabled = false;
submitBtn.textContent = "Log Feeding";
submitBtn.textContent = isEditMode ? "Update Feeding" : "Log Feeding";
}
});