added sleep logging workflow and overview page
This commit is contained in:
@@ -203,6 +203,33 @@
|
||||
.cancel-button:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
.sleep-button {
|
||||
width: 100%;
|
||||
padding: 2rem;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 8px;
|
||||
transition:
|
||||
transform 0.2s,
|
||||
box-shadow 0.2s;
|
||||
}
|
||||
.sleep-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
||||
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
|
||||
}
|
||||
.sleep-button.active {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
}
|
||||
.sleep-button.active:hover {
|
||||
background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%);
|
||||
}
|
||||
.sleep-info {
|
||||
font-size: 0.9rem;
|
||||
margin-top: 0.5rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -218,6 +245,7 @@
|
||||
<div class="menu-item" id="menuAddChild">👶 Add Child</div>
|
||||
<div class="menu-item" id="menuFeedings">🍼 Feedings</div>
|
||||
<div class="menu-item" id="menuDiapers">🧷 Diapers</div>
|
||||
<div class="menu-item" id="menuSleep">😴 Sleep</div>
|
||||
<div class="menu-item logout" id="menuLogout">🚪 Logout</div>
|
||||
</div>
|
||||
|
||||
@@ -261,6 +289,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Start Sleep Modal -->
|
||||
<div class="modal" id="startSleepModal">
|
||||
<div class="modal-content">
|
||||
<h2>😴 Start Sleep</h2>
|
||||
<form id="startSleepForm">
|
||||
<div class="form-group">
|
||||
<label for="modalSleepChildId">Child</label>
|
||||
<select id="modalSleepChildId" required>
|
||||
<option value="">Select a child</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="cancel-button"
|
||||
onclick="closeStartSleepModal()"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit">Start</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const token = localStorage.getItem("access_token");
|
||||
const username = localStorage.getItem("username");
|
||||
@@ -295,6 +348,10 @@
|
||||
window.location.href = "/diapers.html";
|
||||
});
|
||||
|
||||
document.getElementById("menuSleep").addEventListener("click", () => {
|
||||
window.location.href = "/sleep.html";
|
||||
});
|
||||
|
||||
document.getElementById("menuLogout").addEventListener("click", () => {
|
||||
closeMenu();
|
||||
logout();
|
||||
@@ -349,6 +406,9 @@
|
||||
let userChildren = [];
|
||||
let lastFeeding = null;
|
||||
let updateTimerInterval = null;
|
||||
let activeSleep = null;
|
||||
let lastSleep = null;
|
||||
let sleepUpdateTimerInterval = null;
|
||||
|
||||
async function loadFeedingStatus(children) {
|
||||
userChildren = children;
|
||||
@@ -379,12 +439,38 @@
|
||||
lastFeeding = feedings[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch active sleep
|
||||
const activeSleepResponse = await fetch("/api/sleep/active", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (activeSleepResponse.ok) {
|
||||
activeSleep = await activeSleepResponse.json();
|
||||
}
|
||||
|
||||
// Fetch all sleep logs to get the last one
|
||||
const sleepResponse = await fetch("/api/sleep", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (sleepResponse.ok) {
|
||||
const sleeps = await sleepResponse.json();
|
||||
if (sleeps.length > 0) {
|
||||
lastSleep = sleeps[0];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading feeding status:", error);
|
||||
}
|
||||
|
||||
renderContent();
|
||||
startUpdateTimer();
|
||||
startSleepUpdateTimer();
|
||||
}
|
||||
|
||||
function startUpdateTimer() {
|
||||
@@ -406,6 +492,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
function startSleepUpdateTimer() {
|
||||
// Clear any existing timer
|
||||
if (sleepUpdateTimerInterval) {
|
||||
clearInterval(sleepUpdateTimerInterval);
|
||||
}
|
||||
|
||||
// Update every 30 seconds if there's an active sleep
|
||||
if (activeSleep) {
|
||||
sleepUpdateTimerInterval = setInterval(() => {
|
||||
if (activeSleep) {
|
||||
updateSleepTimeDisplay();
|
||||
} else {
|
||||
clearInterval(sleepUpdateTimerInterval);
|
||||
sleepUpdateTimerInterval = null;
|
||||
}
|
||||
}, 30000); // Update every 30 seconds
|
||||
}
|
||||
}
|
||||
|
||||
function updateFeedingTimeDisplay() {
|
||||
const timeElement = document.getElementById("feedingTimeInfo");
|
||||
if (timeElement && activeFeeding) {
|
||||
@@ -413,6 +518,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updateSleepTimeDisplay() {
|
||||
const timeElement = document.getElementById("sleepTimeInfo");
|
||||
if (timeElement && activeSleep) {
|
||||
timeElement.textContent = formatTime(activeSleep.start_time);
|
||||
}
|
||||
}
|
||||
|
||||
function renderContent() {
|
||||
const feedingButtonHtml = activeFeeding
|
||||
? `
|
||||
@@ -430,12 +542,29 @@
|
||||
</button>
|
||||
`;
|
||||
|
||||
const sleepButtonHtml = activeSleep
|
||||
? `
|
||||
<button class="sleep-button active" onclick="stopSleep()">
|
||||
🛑 Stop Sleep
|
||||
<div class="sleep-info">
|
||||
${activeSleep.child_name || "Child"}<br>
|
||||
Started <span id="sleepTimeInfo">${formatTime(activeSleep.start_time)}</span>
|
||||
</div>
|
||||
</button>
|
||||
`
|
||||
: `
|
||||
<button class="sleep-button" onclick="showStartSleepModal()">
|
||||
😴 Start Sleep
|
||||
</button>
|
||||
`;
|
||||
|
||||
document.getElementById("content").innerHTML = `
|
||||
<h1>Welcome to Baby Monitor!</h1>
|
||||
<div class="user-info">
|
||||
<p><strong>Logged in as:</strong> ${username}</p>
|
||||
</div>
|
||||
${feedingButtonHtml}
|
||||
${sleepButtonHtml}
|
||||
<button class="feeding-button" onclick="window.location.href='/log-diaper.html'" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); margin-top: 20px;">
|
||||
🧷 Log Diaper Change
|
||||
</button>
|
||||
@@ -590,6 +719,125 @@
|
||||
}
|
||||
}
|
||||
|
||||
function showStartSleepModal() {
|
||||
const modal = document.getElementById("startSleepModal");
|
||||
const childSelect = document.getElementById("modalSleepChildId");
|
||||
|
||||
// Clear and populate child select
|
||||
childSelect.innerHTML = '<option value="">Select a child</option>';
|
||||
userChildren.forEach((child) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = child.id;
|
||||
option.textContent = child.name;
|
||||
childSelect.appendChild(option);
|
||||
});
|
||||
|
||||
// Set default based on last sleep
|
||||
if (lastSleep) {
|
||||
childSelect.value = lastSleep.child_id;
|
||||
}
|
||||
|
||||
modal.classList.add("show");
|
||||
}
|
||||
|
||||
function closeStartSleepModal() {
|
||||
const modal = document.getElementById("startSleepModal");
|
||||
modal.classList.remove("show");
|
||||
document.getElementById("startSleepForm").reset();
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById("startSleepForm")
|
||||
.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const childId = parseInt(
|
||||
document.getElementById("modalSleepChildId").value,
|
||||
);
|
||||
|
||||
try {
|
||||
const now = new Date();
|
||||
const localDateTime = new Date(
|
||||
now.getTime() - now.getTimezoneOffset() * 60000,
|
||||
)
|
||||
.toISOString()
|
||||
.slice(0, 19);
|
||||
|
||||
const response = await fetch("/api/sleep", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
child_id: childId,
|
||||
start_time: localDateTime,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const sleep = await response.json();
|
||||
activeSleep = sleep;
|
||||
lastSleep = sleep;
|
||||
|
||||
// Add child name to active sleep
|
||||
const child = userChildren.find((c) => c.id === childId);
|
||||
if (child) {
|
||||
activeSleep.child_name = child.name;
|
||||
}
|
||||
|
||||
closeStartSleepModal();
|
||||
renderContent();
|
||||
startSleepUpdateTimer();
|
||||
} else {
|
||||
const data = await response.json();
|
||||
alert(data.detail || "Failed to start sleep");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error starting sleep:", error);
|
||||
alert("Network error. Please try again.");
|
||||
}
|
||||
});
|
||||
|
||||
async function stopSleep() {
|
||||
if (!activeSleep) return;
|
||||
|
||||
try {
|
||||
const now = new Date();
|
||||
const localDateTime = new Date(
|
||||
now.getTime() - now.getTimezoneOffset() * 60000,
|
||||
)
|
||||
.toISOString()
|
||||
.slice(0, 19);
|
||||
|
||||
const response = await fetch(`/api/sleep/${activeSleep.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
end_time: localDateTime,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
activeSleep = null;
|
||||
if (sleepUpdateTimerInterval) {
|
||||
clearInterval(sleepUpdateTimerInterval);
|
||||
sleepUpdateTimerInterval = null;
|
||||
}
|
||||
renderContent();
|
||||
} else {
|
||||
const data = await response.json();
|
||||
alert(data.detail || "Failed to stop sleep");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error stopping sleep:", error);
|
||||
alert("Network error. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
const token = localStorage.getItem("access_token");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user