308 lines
13 KiB
HTML
308 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Fitbit-Garmin Sync Dashboard</title>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
</head>
|
|
<body>
|
|
<div class="container mt-5">
|
|
<h1>Fitbit-Garmin Sync Dashboard</h1>
|
|
|
|
<div class="row mb-4">
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Weight Records</h5>
|
|
<p class="card-text">Total: <span id="total-weights">0</span></p>
|
|
<p class="card-text">Synced: <span id="synced-weights">0</span></p>
|
|
<p class="card-text">Unsynced: <span id="unsynced-weights">0</span></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Activities</h5>
|
|
<p class="card-text">Total: <span id="total-activities">0</span></p>
|
|
<p class="card-text">Downloaded: <span id="downloaded-activities">0</span></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Sync Status</h5>
|
|
<div class="d-grid gap-2">
|
|
<button class="btn btn-primary" type="button" id="sync-weight-btn">Sync Weight</button>
|
|
<button class="btn btn-secondary" type="button" id="sync-activities-btn">Sync Activities</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<h3>Recent Sync Logs</h3>
|
|
<div class="table-responsive">
|
|
<table class="table table-striped" id="sync-logs-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Operation</th>
|
|
<th>Status</th>
|
|
<th>Start Time</th>
|
|
<th>Records Processed</th>
|
|
<th>Records Failed</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td colspan="5">Loading logs...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-5">
|
|
<div class="col-md-12">
|
|
<h3>Health Metrics</h3>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
|
|
<button class="btn btn-info me-md-2" type="button" id="sync-metrics-btn">Sync Health Metrics</button>
|
|
<button class="btn btn-outline-info me-md-2" type="button" id="view-metrics-btn">View Health Data Summary</button>
|
|
<button class="btn btn-outline-info" type="button" id="query-metrics-btn">Query Metrics</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-5">
|
|
<div class="col-md-12">
|
|
<h3>Activity Files</h3>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
|
|
<button class="btn btn-outline-secondary me-md-2" type="button" id="list-activities-btn">List Stored Activities</button>
|
|
<button class="btn btn-outline-secondary" type="button" id="download-activities-btn">Download Activity File</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-5">
|
|
<div class="col-md-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
|
|
<a href="/setup" class="btn btn-primary me-md-2">Setup & Configuration</a>
|
|
<a href="/docs" class="btn btn-outline-secondary" target="_blank">API Documentation</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-5">
|
|
<div class="col-md-12">
|
|
<h3>Health Metrics</h3>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
|
|
<button class="btn btn-info me-md-2" type="button" id="sync-metrics-btn">Sync Health Metrics</button>
|
|
<button class="btn btn-outline-info me-md-2" type="button" id="view-metrics-btn">View Health Data Summary</button>
|
|
<button class="btn btn-outline-info" type="button" id="query-metrics-btn">Query Metrics</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-5">
|
|
<div class="col-md-12">
|
|
<h3>Activity Files</h3>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
|
|
<button class="btn btn-outline-secondary me-md-2" type="button" id="list-activities-btn">List Stored Activities</button>
|
|
<button class="btn btn-outline-secondary" type="button" id="download-activities-btn">Download Activity File</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
// Load dashboard data when page loads
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadDashboardData();
|
|
|
|
// Set up sync buttons
|
|
document.getElementById('sync-weight-btn').addEventListener('click', syncWeight);
|
|
document.getElementById('sync-activities-btn').addEventListener('click', syncActivities);
|
|
|
|
// Set up metrics buttons
|
|
document.getElementById('sync-metrics-btn').addEventListener('click', syncHealthMetrics);
|
|
document.getElementById('view-metrics-btn').addEventListener('click', viewHealthSummary);
|
|
document.getElementById('query-metrics-btn').addEventListener('click', queryMetrics);
|
|
|
|
// Set up activity file buttons
|
|
document.getElementById('list-activities-btn').addEventListener('click', listActivities);
|
|
document.getElementById('download-activities-btn').addEventListener('click', downloadActivityFile);
|
|
});
|
|
|
|
async function loadDashboardData() {
|
|
try {
|
|
const response = await fetch('/api/status');
|
|
const data = await response.json();
|
|
|
|
document.getElementById('total-weights').textContent = data.total_weight_records;
|
|
document.getElementById('synced-weights').textContent = data.synced_weight_records;
|
|
document.getElementById('unsynced-weights').textContent = data.unsynced_weight_records;
|
|
document.getElementById('total-activities').textContent = data.total_activities;
|
|
document.getElementById('downloaded-activities').textContent = data.downloaded_activities;
|
|
|
|
// Update logs table
|
|
const logsBody = document.querySelector('#sync-logs-table tbody');
|
|
logsBody.innerHTML = '';
|
|
|
|
data.recent_logs.forEach(log => {
|
|
const row = document.createElement('tr');
|
|
row.innerHTML = `
|
|
<td>${log.operation}</td>
|
|
<td>${log.status}</td>
|
|
<td>${log.start_time}</td>
|
|
<td>${log.records_processed}</td>
|
|
<td>${log.records_failed}</td>
|
|
`;
|
|
logsBody.appendChild(row);
|
|
});
|
|
} catch (error) {
|
|
console.error('Error loading dashboard data:', error);
|
|
}
|
|
}
|
|
|
|
async function syncWeight() {
|
|
try {
|
|
const response = await fetch('/api/sync/weight', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
alert(`Weight sync initiated: ${data.message}`);
|
|
|
|
// Refresh dashboard data
|
|
loadDashboardData();
|
|
} catch (error) {
|
|
console.error('Error syncing weight:', error);
|
|
alert('Error initiating weight sync: ' + error.message);
|
|
}
|
|
}
|
|
|
|
async function syncActivities() {
|
|
try {
|
|
const response = await fetch('/api/sync/activities', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ days_back: 30 })
|
|
});
|
|
|
|
const data = await response.json();
|
|
alert(`Activity sync initiated: ${data.message}`);
|
|
|
|
// Refresh dashboard data
|
|
loadDashboardData();
|
|
} catch (error) {
|
|
console.error('Error syncing activities:', error);
|
|
alert('Error initiating activity sync: ' + error.message);
|
|
}
|
|
}
|
|
|
|
async function syncHealthMetrics() {
|
|
try {
|
|
const response = await fetch('/api/sync/metrics', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
alert(`Health metrics sync initiated: ${data.message}`);
|
|
} catch (error) {
|
|
console.error('Error syncing health metrics:', error);
|
|
alert('Error initiating health metrics sync: ' + error.message);
|
|
}
|
|
}
|
|
|
|
async function viewHealthSummary() {
|
|
try {
|
|
const response = await fetch('/api/health-data/summary');
|
|
const data = await response.json();
|
|
|
|
alert(`Health Summary:
|
|
Steps: ${data.total_steps || 0}
|
|
Avg Heart Rate: ${data.avg_heart_rate || 0}
|
|
Sleep Hours: ${data.total_sleep_hours || 0}
|
|
Avg Calories: ${data.avg_calories || 0}`);
|
|
} catch (error) {
|
|
console.error('Error fetching health summary:', error);
|
|
alert('Error fetching health summary: ' + error.message);
|
|
}
|
|
}
|
|
|
|
async function queryMetrics() {
|
|
try {
|
|
const response = await fetch('/api/metrics/query');
|
|
const data = await response.json();
|
|
|
|
alert(`Found ${data.length} health metrics`);
|
|
} catch (error) {
|
|
console.error('Error querying metrics:', error);
|
|
alert('Error querying metrics: ' + error.message);
|
|
}
|
|
}
|
|
|
|
async function listActivities() {
|
|
try {
|
|
const response = await fetch('/api/activities/list');
|
|
const data = await response.json();
|
|
|
|
alert(`Found ${data.length} stored activities`);
|
|
} catch (error) {
|
|
console.error('Error listing activities:', error);
|
|
alert('Error listing activities: ' + error.message);
|
|
}
|
|
}
|
|
|
|
async function downloadActivityFile() {
|
|
try {
|
|
// For demo purposes, we'll use a placeholder ID
|
|
// In a real implementation, this would prompt for activity ID or list available activities
|
|
const activityId = prompt('Enter activity ID to download:', '12345');
|
|
if (activityId) {
|
|
// This would initiate a download of the stored activity file
|
|
window.open(`/api/activities/download/${activityId}`, '_blank');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error downloading activity file:', error);
|
|
alert('Error downloading activity file: ' + error.message);
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |