Files
HydroFlux/js/modules/fitness.js
2026-02-04 20:56:57 +11:00

154 lines
6.7 KiB
JavaScript

export class FitnessDashboard {
constructor(containerId) {
this.container = document.getElementById(containerId);
// Mock Data
this.data = {
steps: { current: 8432, goal: 10000 },
sleep: { current: 6.5, goal: 8 },
history: {
steps: [4500, 7200, 10500, 8900, 6000, 11200, 8432],
sleep: [5.5, 6.0, 7.5, 8.2, 5.0, 9.0, 6.5]
}
};
this.render();
// Delay animation to allow DOM paint
setTimeout(() => this.animate(), 100);
}
render() {
const stepPercent = Math.min((this.data.steps.current / this.data.steps.goal) * 100, 100);
const sleepPercent = Math.min((this.data.sleep.current / this.data.sleep.goal) * 100, 100);
// Ring Config
const center = 100;
const radiusOuter = 80;
const radiusInner = 55;
const circumOuter = 2 * Math.PI * radiusOuter;
const circumInner = 2 * Math.PI * radiusInner;
this.container.innerHTML = `
<div class="fitness-container">
<h2 class="section-title">DAILY ACTIVITY</h2>
<!-- Top: Concentric Rings -->
<div class="rings-wrapper">
<svg class="concentric-svg" viewBox="0 0 200 200">
<!-- Outer Track (Steps) -->
<circle class="ring-bg" cx="${center}" cy="${center}" r="${radiusOuter}" stroke-width="18"></circle>
<!-- Inner Track (Sleep) -->
<circle class="ring-bg" cx="${center}" cy="${center}" r="${radiusInner}" stroke-width="18"></circle>
<!-- Outer Progress (Steps - Cyan) -->
<circle class="ring-progress cyan" cx="${center}" cy="${center}" r="${radiusOuter}"
stroke-width="18"
stroke-dasharray="${circumOuter}"
stroke-dashoffset="${circumOuter}"
data-offset="${circumOuter - (stepPercent / 100) * circumOuter}"></circle>
<!-- Inner Progress (Sleep - Purple) -->
<circle class="ring-progress purple" cx="${center}" cy="${center}" r="${radiusInner}"
stroke-width="18"
stroke-dasharray="${circumInner}"
stroke-dashoffset="${circumInner}"
data-offset="${circumInner - (sleepPercent / 100) * circumInner}"></circle>
<!-- Icons/Center -->
<image href="https://fonts.gstatic.com/s/i/materialicons/bolt/v5/24px.svg" x="90" y="90" height="20" width="20" style="filter: invert(1); opacity: 0.5;" />
</svg>
<!-- Legend to explain the rings -->
<div class="rings-legend">
<div class="legend-item">
<span class="dot cyan"></span> STEPS
</div>
<div class="legend-item">
<span class="dot purple"></span> SLEEP
</div>
</div>
</div>
<!-- Middle: Device -->
<div class="device-card">
<div class="device-info">
<span class="device-name">TicWatch Pro 5</span>
<span class="device-status">Disconnected</span>
</div>
<button id="connect-watch-btn" class="connect-glow-btn">LINK</button>
</div>
<!-- Box 1: Steps (Current + History) -->
<div class="stat-card">
<div class="stat-header">
<div>
<span class="stat-label">STEPS</span>
<div class="stat-value">${this.data.steps.current}</div>
<div class="stat-sub">Goal: ${this.data.steps.goal}</div>
</div>
<div class="icon-box cyan-box">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
</div>
</div>
<div class="chart-divider"></div>
<div class="chart-container small">
${this.generateBars(this.data.history.steps, 12000, 'cyan')}
</div>
</div>
<!-- Box 2: Sleep (Current + History) -->
<div class="stat-card">
<div class="stat-header">
<div>
<span class="stat-label">SLEEP</span>
<div class="stat-value">${this.data.sleep.current}h</div>
<div class="stat-sub">Goal: ${this.data.sleep.goal}h</div>
</div>
<div class="icon-box purple-box">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
</div>
</div>
<div class="chart-divider"></div>
<div class="chart-container small">
${this.generateBars(this.data.history.sleep, 10, 'purple')}
</div>
</div>
</div>
`;
this.attachEvents();
}
generateBars(data, max, colorClass) {
const days = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
return data.map((val, index) => {
const height = Math.min((val / max) * 100, 100);
return `
<div class="chart-column">
<div class="chart-bar ${colorClass}" style="height: ${height}%"></div>
<span class="chart-day">${days[index]}</span>
</div>
`;
}).join('');
}
animate() {
this.container.querySelectorAll('.ring-progress').forEach(ring => {
ring.style.strokeDashoffset = ring.dataset.offset;
});
}
attachEvents() {
const btn = this.container.querySelector('#connect-watch-btn');
if (btn) {
btn.addEventListener('click', () => {
alert("Placeholder: This feature would connect to the WearOS API in the native app.");
});
}
}
}