Add supplements, kettlebell, calendar, push notifications, and PWA support

- Supplement tracking: CRUD endpoints, /today, /logs, Supplements page
- Kettlebell workouts: session tracking, analytics endpoint, ActiveSession page
- Calendar module: events CRUD, calendar components
- Push notifications: VAPID keys, PushSubscription model, APScheduler reminders,
  service worker with push/notificationclick handlers, Profile notifications UI
- PWA: vite-plugin-pwa, manifest, icons, service worker generation
- Frontend: TypeScript types, API modules, ConfirmModal, toast notifications
- Auth fixes: password hashing, nutrition endpoint auth
- CLAUDE.md: project documentation and development guide

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Carlos Escalante
2026-03-20 18:57:03 -06:00
parent bd91eb4171
commit f279907ae3
61 changed files with 9256 additions and 85 deletions

View File

@@ -0,0 +1,39 @@
self.addEventListener('push', (event) => {
console.log('[SW] push received', event.data?.text());
if (!event.data) return;
let title = 'HealthyFit';
let body = 'You have a new notification';
let url = '/';
try {
const data = event.data.json();
title = data.title;
body = data.body;
url = data.url ?? '/';
} catch {
body = event.data.text();
}
event.waitUntil(
self.registration.showNotification(title, {
body,
icon: '/icons/icon-192.png',
badge: '/icons/icon-192.png',
data: { url },
})
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
const url = event.notification.data?.url ?? '/';
event.waitUntil(
self.clients
.matchAll({ type: 'window', includeUncontrolled: true })
.then((clients) => {
const existing = clients.find((c) => c.url.includes(url));
if (existing) return existing.focus();
return self.clients.openWindow(url);
})
);
});