Files
FitTrack2/FitnessSync/backend/templates/index.html

210 lines
8.9 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>
<!-- Toast container for notifications -->
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div id="appToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto" id="toast-title">Notification</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body" id="toast-body">
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-6">
<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-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Sync Controls</h5>
<div class="d-grid gap-2">
<button class="btn btn-primary" type="button" id="sync-activities-btn">Sync Activities</button>
<button class="btn btn-info" type="button" id="sync-metrics-btn">Sync Health Metrics</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>End Time</th>
<th>Processed</th>
<th>Failed</th>
<th>Message</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="7">Loading logs...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="row mt-5">
<div class="col-md-12">
<h3>Actions</h5>
<div class="card">
<div class="card-body">
<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>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
let toastInstance = null;
document.addEventListener('DOMContentLoaded', function() {
const toastEl = document.getElementById('appToast');
toastInstance = new bootstrap.Toast(toastEl);
loadDashboardData();
document.getElementById('sync-activities-btn').addEventListener('click', syncActivities);
document.getElementById('sync-metrics-btn').addEventListener('click', syncHealthMetrics);
});
function showToast(title, body, level = 'info') {
const toastTitle = document.getElementById('toast-title');
const toastBody = document.getElementById('toast-body');
const toastHeader = document.querySelector('.toast-header');
toastTitle.textContent = title;
toastBody.textContent = body;
// Reset header color
toastHeader.classList.remove('bg-success', 'bg-danger', 'bg-warning', 'bg-info', 'text-white');
if (level === 'success') {
toastHeader.classList.add('bg-success', 'text-white');
} else if (level === 'error') {
toastHeader.classList.add('bg-danger', 'text-white');
} else if (level === 'warning') {
toastHeader.classList.add('bg-warning');
} else {
toastHeader.classList.add('bg-info', 'text-white');
}
toastInstance.show();
}
async function loadDashboardData() {
try {
const response = await fetch('/api/status');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
document.getElementById('total-activities').textContent = data.total_activities;
document.getElementById('downloaded-activities').textContent = data.downloaded_activities;
const logsBody = document.querySelector('#sync-logs-table tbody');
logsBody.innerHTML = '';
if (data.recent_logs.length === 0) {
logsBody.innerHTML = '<tr><td colspan="7">No recent sync logs.</td></tr>';
return;
}
data.recent_logs.forEach(log => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${log.operation}</td>
<td><span class="badge bg-${log.status === 'completed' ? 'success' : 'warning'}">${log.status}</span></td>
<td>${new Date(log.start_time).toLocaleString()}</td>
<td>${log.end_time ? new Date(log.end_time).toLocaleString() : 'N/A'}</td>
<td>${log.records_processed}</td>
<td>${log.records_failed}</td>
<td>${log.message || ''}</td>
`;
logsBody.appendChild(row);
});
} catch (error) {
console.error('Error loading dashboard data:', error);
showToast('Error', 'Could not load dashboard data.', 'error');
}
}
async function syncActivities() {
showToast('Syncing...', 'Activity sync has been initiated.', 'info');
try {
const response = await fetch('/api/sync/activities', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ days_back: 30 })
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || `HTTP error! status: ${response.status}`);
}
const data = await response.json();
showToast('Sync Complete', data.message, 'success');
loadDashboardData(); // Refresh data after sync
} catch (error) {
console.error('Error syncing activities:', error);
showToast('Sync Error', `Activity sync failed: ${error.message}`, 'error');
}
}
async function syncHealthMetrics() {
showToast('Syncing...', 'Health metrics sync has been initiated.', 'info');
try {
const response = await fetch('/api/sync/metrics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || `HTTP error! status: ${response.status}`);
}
const data = await response.json();
showToast('Sync Complete', data.message, 'success');
loadDashboardData(); // Refresh data after sync
} catch (error) {
console.error('Error syncing health metrics:', error);
showToast('Sync Error', `Health metrics sync failed: ${error.message}`, 'error');
}
}
</script>
</body>
</html>