928 lines
28 KiB
HTML
928 lines
28 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Baby Monitor</title>
|
||
<link rel="stylesheet" href="/menu.css" />
|
||
<style>
|
||
body {
|
||
font-family: Arial, sans-serif;
|
||
max-width: 800px;
|
||
margin: 2rem auto;
|
||
padding: 0 1rem;
|
||
background-color: #f0f0f0;
|
||
}
|
||
.container {
|
||
background: white;
|
||
padding: 2rem;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||
}
|
||
h1 {
|
||
color: #333;
|
||
margin-bottom: 1rem;
|
||
}
|
||
.greeting {
|
||
color: #666;
|
||
font-size: 1.1rem;
|
||
margin-bottom: 2rem;
|
||
}
|
||
button {
|
||
padding: 0.75rem 1.5rem;
|
||
background-color: #dc3545;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 1rem;
|
||
margin-right: 0.5rem;
|
||
}
|
||
button:hover {
|
||
background-color: #c82333;
|
||
}
|
||
.admin-button {
|
||
background-color: #667eea;
|
||
}
|
||
.admin-button:hover {
|
||
background-color: #5568d3;
|
||
}
|
||
.hidden {
|
||
display: none;
|
||
}
|
||
.loading {
|
||
text-align: center;
|
||
padding: 2rem;
|
||
}
|
||
.feeding-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;
|
||
}
|
||
.feeding-button:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
||
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
|
||
}
|
||
.feeding-button.active {
|
||
background: linear-gradient(135deg, #f44336 0%, #e91e63 100%);
|
||
}
|
||
.feeding-button.active:hover {
|
||
background: linear-gradient(135deg, #e91e63 0%, #f44336 100%);
|
||
}
|
||
.feeding-info {
|
||
font-size: 0.9rem;
|
||
margin-top: 0.5rem;
|
||
opacity: 0.9;
|
||
}
|
||
.modal {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
z-index: 1001;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
.modal.show {
|
||
display: flex;
|
||
}
|
||
.modal-content {
|
||
background: white;
|
||
padding: 2rem;
|
||
border-radius: 8px;
|
||
max-width: 400px;
|
||
width: 90%;
|
||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
|
||
}
|
||
.modal h2 {
|
||
margin-top: 0;
|
||
margin-bottom: 1.5rem;
|
||
color: #333;
|
||
}
|
||
.form-group {
|
||
margin-bottom: 1rem;
|
||
}
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 0.5rem;
|
||
color: #333;
|
||
font-weight: 600;
|
||
}
|
||
.form-group select {
|
||
width: 100%;
|
||
padding: 0.75rem;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
font-size: 1rem;
|
||
}
|
||
.modal-buttons {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
margin-top: 1.5rem;
|
||
}
|
||
.modal-buttons button {
|
||
flex: 1;
|
||
margin: 0;
|
||
}
|
||
.cancel-button {
|
||
background-color: #6c757d;
|
||
}
|
||
.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;
|
||
}
|
||
.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;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div id="content" class="loading">
|
||
<p>Loading...</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Start Feeding Modal -->
|
||
<div class="modal" id="startFeedingModal">
|
||
<div class="modal-content">
|
||
<h2>🍼 Start Feeding</h2>
|
||
<form id="startFeedingForm">
|
||
<div class="form-group">
|
||
<label for="modalChildId">Child</label>
|
||
<select id="modalChildId" required>
|
||
<option value="">Select a child</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="modalFeedingType">Feeding Type</label>
|
||
<select id="modalFeedingType" required>
|
||
<option value="">Select type</option>
|
||
<option value="left_breast">Left Breast</option>
|
||
<option value="right_breast">Right Breast</option>
|
||
<option value="bottle">Bottle</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-buttons">
|
||
<button
|
||
type="button"
|
||
class="cancel-button"
|
||
onclick="closeStartFeedingModal()"
|
||
>
|
||
Cancel
|
||
</button>
|
||
<button type="submit">Start</button>
|
||
</div>
|
||
</form>
|
||
</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 src="/menu.js"></script>
|
||
<script>
|
||
const token = localStorage.getItem("access_token");
|
||
const username = localStorage.getItem("username");
|
||
|
||
// Check if user is logged in
|
||
if (!token) {
|
||
window.location.href = "/login.html";
|
||
} else {
|
||
// Fetch current user info including admin status
|
||
fetch("/api/me", {
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
})
|
||
.then((response) => {
|
||
if (response.ok) {
|
||
return response.json();
|
||
} else {
|
||
// Token invalid, redirect to login
|
||
localStorage.removeItem("access_token");
|
||
localStorage.removeItem("username");
|
||
window.location.href = "/login.html";
|
||
throw new Error("Authentication failed");
|
||
}
|
||
})
|
||
.then((user) => {
|
||
// Initialize burger menu
|
||
initBurgerMenu({
|
||
includeHome: true,
|
||
});
|
||
|
||
// Check if user has any children
|
||
return fetch("/api/children", {
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
})
|
||
.then((response) => response.json())
|
||
.then((children) => {
|
||
// If no children, show welcome message with option to add
|
||
if (children.length === 0) {
|
||
showNoChildrenMessage();
|
||
return;
|
||
}
|
||
|
||
// Load feeding status and show content
|
||
loadFeedingStatus(children);
|
||
});
|
||
})
|
||
.catch((error) => {
|
||
console.error("Error:", error);
|
||
});
|
||
}
|
||
|
||
function showNoChildrenMessage() {
|
||
const content = document.getElementById("content");
|
||
content.className = "";
|
||
content.innerHTML = `
|
||
<h1>👶 Welcome to Baby Monitor</h1>
|
||
<div style="text-align: center; padding: 40px 20px;">
|
||
<div style="font-size: 64px; margin-bottom: 20px;">🍼</div>
|
||
<h2 style="color: #333; margin-bottom: 15px;">No Children Added Yet</h2>
|
||
<p style="color: #666; margin-bottom: 30px; line-height: 1.6;">
|
||
You can add a child to start tracking or redeem a child share code<br>
|
||
from another user to access their child's information.
|
||
</p>
|
||
<div style="display: flex; gap: 15px; justify-content: center; flex-wrap: wrap;">
|
||
<a href="/add-child.html" class="button" style="text-decoration: none; display: inline-block;">
|
||
➕ Add Your Child
|
||
</a>
|
||
<a href="/settings.html" class="button secondary" style="text-decoration: none; display: inline-block;">
|
||
🔗 Redeem Share Code
|
||
</a>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
let activeFeeding = null;
|
||
let userChildren = [];
|
||
let lastFeeding = null;
|
||
let updateTimerInterval = null;
|
||
let activeSleep = null;
|
||
let lastSleep = null;
|
||
let sleepUpdateTimerInterval = null;
|
||
let lastDiaperChange = null;
|
||
|
||
async function loadFeedingStatus(children) {
|
||
userChildren = children;
|
||
|
||
try {
|
||
// Fetch active feeding
|
||
const activeResponse = await fetch("/api/feedings/active", {
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
});
|
||
|
||
if (activeResponse.ok) {
|
||
activeFeeding = await activeResponse.json();
|
||
}
|
||
|
||
// Fetch all feedings to get the last one
|
||
const feedingsResponse = await fetch("/api/feedings", {
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
});
|
||
|
||
if (feedingsResponse.ok) {
|
||
const feedings = await feedingsResponse.json();
|
||
if (feedings.length > 0) {
|
||
// Feedings are ordered by start_time desc, so first is most recent
|
||
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];
|
||
}
|
||
}
|
||
|
||
// 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);
|
||
}
|
||
|
||
renderContent();
|
||
startUpdateTimer();
|
||
startSleepUpdateTimer();
|
||
}
|
||
|
||
function startUpdateTimer() {
|
||
// Clear any existing timer
|
||
if (updateTimerInterval) {
|
||
clearInterval(updateTimerInterval);
|
||
}
|
||
|
||
// Update every 30 seconds if there's an active feeding
|
||
if (activeFeeding) {
|
||
updateTimerInterval = setInterval(() => {
|
||
if (activeFeeding) {
|
||
updateFeedingTimeDisplay();
|
||
} else {
|
||
clearInterval(updateTimerInterval);
|
||
updateTimerInterval = null;
|
||
}
|
||
}, 30000); // Update every 30 seconds
|
||
}
|
||
}
|
||
|
||
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) {
|
||
timeElement.textContent = formatTime(activeFeeding.start_time);
|
||
}
|
||
}
|
||
|
||
function updateSleepTimeDisplay() {
|
||
const timeElement = document.getElementById("sleepTimeInfo");
|
||
if (timeElement && activeSleep) {
|
||
timeElement.textContent = formatTime(activeSleep.start_time);
|
||
}
|
||
}
|
||
|
||
function renderContent() {
|
||
const feedingButtonHtml = activeFeeding
|
||
? `
|
||
<button class="feeding-button active" onclick="stopFeeding()">
|
||
🛑 Stopped Feeding
|
||
<div class="feeding-info">
|
||
${activeFeeding.child_name || "Child"} - ${formatFeedingType(activeFeeding.feeding_type)}<br>
|
||
Started <span id="feedingTimeInfo">${formatTime(activeFeeding.start_time)}</span>
|
||
</div>
|
||
</button>
|
||
`
|
||
: `
|
||
<button class="feeding-button" onclick="window.location.href='/log-feeding.html'">
|
||
🍼 Started Feeding
|
||
</button>
|
||
`;
|
||
|
||
const sleepButtonHtml = activeSleep
|
||
? `
|
||
<button class="sleep-button active" onclick="stopSleep()">
|
||
🛑 Stopped Sleeping
|
||
<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()">
|
||
😴 Started Sleeping
|
||
</button>
|
||
`;
|
||
|
||
document.getElementById("content").innerHTML = `
|
||
<h1>Hi ${username}</h1>
|
||
<p class="greeting">Let's keep track of your baby's care.</p>
|
||
<button class="feeding-button" onclick="window.location.href='/log-diaper.html'" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); margin-bottom: 20px;">
|
||
🧷 Changed Diaper
|
||
</button>
|
||
${feedingButtonHtml}
|
||
${sleepButtonHtml}
|
||
${buildRecentActivitySection()}
|
||
`;
|
||
}
|
||
|
||
function buildRecentActivitySection() {
|
||
// Build diaper card
|
||
const diaperCard = lastDiaperChange
|
||
? `
|
||
<a href="/diapers.html" class="activity-card" style="text-decoration:none; color:inherit;">
|
||
<div class="icon">🧷</div>
|
||
<div class="type">Last Diaper</div>
|
||
<div class="time">${formatTime(lastDiaperChange.change_time)}</div>
|
||
<div class="details">
|
||
${lastDiaperChange.poop_amount ? `💩 ${lastDiaperChange.poop_amount}` : ""}
|
||
${lastDiaperChange.poop_amount && lastDiaperChange.pee_amount ? " • " : ""}
|
||
${lastDiaperChange.pee_amount ? `💧 ${lastDiaperChange.pee_amount}` : ""}
|
||
</div>
|
||
</a>
|
||
`
|
||
: `
|
||
<a href="/diapers.html" class="activity-card empty" style="text-decoration:none; color:inherit;">
|
||
<div class="icon">🧷</div>
|
||
<div class="type">No diaper changes yet</div>
|
||
</a>
|
||
`;
|
||
|
||
// Build feeding card
|
||
const feedingCard = lastFeeding
|
||
? `
|
||
<a href="/feedings.html" class="activity-card" style="text-decoration:none; color:inherit;">
|
||
<div class="icon">🍼</div>
|
||
<div class="type">Last Feeding</div>
|
||
<div class="time">${formatTime(lastFeeding.start_time)}</div>
|
||
<div class="details">
|
||
${formatFeedingType(lastFeeding.feeding_type)}
|
||
${lastFeeding.end_time ? ` • ${calculateDuration(lastFeeding.start_time, lastFeeding.end_time)}` : " (ongoing)"}
|
||
</div>
|
||
</a>
|
||
`
|
||
: `
|
||
<a href="/feedings.html" class="activity-card empty" style="text-decoration:none; color:inherit;">
|
||
<div class="icon">🍼</div>
|
||
<div class="type">No feedings yet</div>
|
||
</a>
|
||
`;
|
||
|
||
// Build sleep card
|
||
const sleepCard = lastSleep
|
||
? `
|
||
<a href="/sleep.html" class="activity-card" style="text-decoration:none; color:inherit;">
|
||
<div class="icon">😴</div>
|
||
<div class="type">Last Sleep</div>
|
||
<div class="time">${formatTime(lastSleep.start_time)}</div>
|
||
<div class="details">
|
||
${lastSleep.end_time ? calculateDuration(lastSleep.start_time, lastSleep.end_time) : "ongoing"}
|
||
</div>
|
||
</a>
|
||
`
|
||
: `
|
||
<a href="/sleep.html" class="activity-card empty" style="text-decoration:none; color:inherit;">
|
||
<div class="icon">😴</div>
|
||
<div class="type">No sleep sessions yet</div>
|
||
</a>
|
||
`;
|
||
|
||
return `
|
||
<div class="recent-activity">
|
||
<h3>📊 Recent Activity</h3>
|
||
<div class="activity-grid">
|
||
${diaperCard}
|
||
${feedingCard}
|
||
${sleepCard}
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function calculateDuration(startTime, endTime) {
|
||
const start = new Date(startTime);
|
||
const end = new Date(endTime);
|
||
const diffMs = end - start;
|
||
const diffMins = Math.floor(diffMs / 60000);
|
||
|
||
if (diffMins < 60) {
|
||
return `${diffMins} min`;
|
||
}
|
||
|
||
const hours = Math.floor(diffMins / 60);
|
||
const mins = diffMins % 60;
|
||
return `${hours}h ${mins}m`;
|
||
}
|
||
|
||
function formatFeedingType(type) {
|
||
const types = {
|
||
left_breast: "Left Breast",
|
||
right_breast: "Right Breast",
|
||
bottle: "Bottle",
|
||
};
|
||
return types[type] || type;
|
||
}
|
||
|
||
function formatTime(dateString) {
|
||
const date = new Date(dateString);
|
||
const now = new Date();
|
||
const diffMs = now - date;
|
||
const diffMins = Math.floor(diffMs / 60000);
|
||
|
||
if (diffMins < 1) return "just now";
|
||
if (diffMins < 60) return `${diffMins} min ago`;
|
||
|
||
const diffHours = Math.floor(diffMins / 60);
|
||
if (diffHours < 24) return `${diffHours}h ${diffMins % 60}m ago`;
|
||
|
||
return date.toLocaleString();
|
||
}
|
||
|
||
function showStartFeedingModal() {
|
||
const modal = document.getElementById("startFeedingModal");
|
||
const childSelect = document.getElementById("modalChildId");
|
||
const typeSelect = document.getElementById("modalFeedingType");
|
||
|
||
// Clear and populate child select or show single child
|
||
if (userChildren.length === 1) {
|
||
// Single child: replace dropdown with text display
|
||
const child = userChildren[0];
|
||
const formGroup = childSelect.parentElement;
|
||
formGroup.innerHTML = `
|
||
<label for="modalChildName">Child</label>
|
||
<input
|
||
type="text"
|
||
id="modalChildName"
|
||
value="${child.name}"
|
||
readonly
|
||
style="background-color: #f5f5f5; cursor: default;"
|
||
/>
|
||
<input type="hidden" id="modalChildId" value="${child.id}" />
|
||
`;
|
||
} else {
|
||
// Multiple children: show dropdown
|
||
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 defaults based on last feeding
|
||
if (lastFeeding) {
|
||
childSelect.value = lastFeeding.child_id;
|
||
}
|
||
}
|
||
|
||
// Set default feeding type based on last feeding
|
||
if (lastFeeding) {
|
||
typeSelect.value = lastFeeding.feeding_type;
|
||
}
|
||
|
||
modal.classList.add("show");
|
||
}
|
||
|
||
function closeStartFeedingModal() {
|
||
const modal = document.getElementById("startFeedingModal");
|
||
modal.classList.remove("show");
|
||
document.getElementById("startFeedingForm").reset();
|
||
}
|
||
|
||
document
|
||
.getElementById("startFeedingForm")
|
||
.addEventListener("submit", async (e) => {
|
||
e.preventDefault();
|
||
|
||
const childId = parseInt(
|
||
document.getElementById("modalChildId").value,
|
||
);
|
||
const feedingType = document.getElementById("modalFeedingType").value;
|
||
|
||
try {
|
||
const now = new Date();
|
||
const localDateTime = new Date(
|
||
now.getTime() - now.getTimezoneOffset() * 60000,
|
||
)
|
||
.toISOString()
|
||
.slice(0, 19);
|
||
|
||
const response = await fetch("/api/feedings", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
body: JSON.stringify({
|
||
child_id: childId,
|
||
start_time: localDateTime,
|
||
end_time: null,
|
||
feeding_type: feedingType,
|
||
}),
|
||
});
|
||
|
||
if (response.ok) {
|
||
const feeding = await response.json();
|
||
activeFeeding = feeding;
|
||
lastFeeding = feeding; // Update last feeding
|
||
|
||
// Add child name to active feeding
|
||
const child = userChildren.find((c) => c.id === childId);
|
||
if (child) {
|
||
activeFeeding.child_name = child.name;
|
||
}
|
||
|
||
closeStartFeedingModal();
|
||
renderContent();
|
||
startUpdateTimer(); // Start the timer for live updates
|
||
} else {
|
||
const data = await response.json();
|
||
alert(data.detail || "Failed to start feeding");
|
||
}
|
||
} catch (error) {
|
||
console.error("Error starting feeding:", error);
|
||
alert("Network error. Please try again.");
|
||
}
|
||
});
|
||
|
||
async function stopFeeding() {
|
||
if (!activeFeeding) return;
|
||
|
||
try {
|
||
const now = new Date();
|
||
const localDateTime = new Date(
|
||
now.getTime() - now.getTimezoneOffset() * 60000,
|
||
)
|
||
.toISOString()
|
||
.slice(0, 19);
|
||
|
||
const response = await fetch(`/api/feedings/${activeFeeding.id}`, {
|
||
method: "PUT",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
body: JSON.stringify({
|
||
end_time: localDateTime,
|
||
}),
|
||
});
|
||
|
||
if (response.ok) {
|
||
activeFeeding = null;
|
||
if (updateTimerInterval) {
|
||
clearInterval(updateTimerInterval);
|
||
updateTimerInterval = null;
|
||
}
|
||
renderContent();
|
||
} else {
|
||
const data = await response.json();
|
||
alert(data.detail || "Failed to stop feeding");
|
||
}
|
||
} catch (error) {
|
||
console.error("Error stopping feeding:", error);
|
||
alert("Network error. Please try again.");
|
||
}
|
||
}
|
||
|
||
function showStartSleepModal() {
|
||
window.location.href = "/log-sleep.html";
|
||
}
|
||
|
||
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");
|
||
|
||
try {
|
||
await fetch("/api/logout", {
|
||
method: "POST",
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
});
|
||
} catch (error) {
|
||
console.error("Logout error:", error);
|
||
} finally {
|
||
// Clear local storage and redirect
|
||
localStorage.removeItem("access_token");
|
||
localStorage.removeItem("username");
|
||
window.location.href = "/login.html";
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|