Completion Certificate
Download and email your completion certificate once you finish all 30 days.
{certificateMessage}
: null}/* @component-map * App β Main container, tab navigation [app.jsx] * ChallengeTracker β Daily challenge grid with check-off [components/ChallengeTracker.jsx] * Leaderboard β Group leaderboard of completion percentages [components/Leaderboard.jsx] * ChallengeSetup β Admin view to define daily challenges [components/ChallengeSetup.jsx] * @end-component-map */ // DEPLOY_CONFIG: {"triggers": [{"name": "30_day_completion_certificate", "on": "collection.update", "collection": "users", "actions": [{"type": "event", "event_type": "group.notify", "payload": {"message": "A user has completed all 30 days of the challenge."}}, {"type": "email", "to": "{{user.email}}", "subject": "Congratulations on completing all 30 days!", "body": "Attached is your PDF certificate of completion."}]}]} import { useState, useEffect, useMemo, useRef } from 'react'; import { useAuth, usePresence, useCollection, useAI, sendEmail, generatePDF } from '@deplixo/sdk'; import { ChallengeTracker } from './components/ChallengeTracker.jsx'; import { Leaderboard } from './components/Leaderboard.jsx'; import { ChallengeSetup } from './components/ChallengeSetup.jsx'; function App() { const [tab, setTab] = useState('challenge'); const [certificateStatus, setCertificateStatus] = useState('idle'); const [certificateMessage, setCertificateMessage] = useState(''); const certificateRef = useRef(null); const { user, loading, logout } = useAuth(); const { items: challengeItems, add: addChallenge, update: updateChallenge, loading: challengesLoading } = useCollection('daily_challenges', { personal: false }); const { items: settingsItems, add: addSetting, update: updateSetting, loading: settingsLoading } = useCollection('challenge_settings', { personal: false }); const { items: completionItems } = useCollection('challenge_completions', { personal: true }); const { generate: generateAI } = useAI(); const selectedLevel = settingsItems?.[0]?.selectedLevel || 'intermediate'; const challengePack = challengeItems || []; const completions = completionItems || []; const completedCount = completions.filter((item) => item.completed).length; const completionPercent = Math.round((completedCount / 30) * 100); const isComplete = completedCount >= 30; const hasChallenges = challengePack.length > 0; useEffect(() => { if (!user) return; if (!settingsItems || settingsItems.length === 0) { addSetting({ selectedLevel: 'intermediate', updatedBy: user.id, updatedByName: user.name, updatedAt: Date.now(), }); } }, [user, settingsItems, addSetting]); useEffect(() => { if (!user || !hasChallenges) return; const needsLevelSync = challengePack.some((item) => !item.fitnessLevel || !item.variation || !item.aiGenerated); if (!needsLevelSync) return; challengePack.forEach((item) => { if (!item.fitnessLevel || !item.variation || !item.aiGenerated) { updateChallenge(item.id, { fitnessLevel: item.fitnessLevel || selectedLevel, variation: item.variation || 'intermediate', aiGenerated: true, }); } }); }, [user, hasChallenges, challengePack, selectedLevel, updateChallenge]); const ensureDailyChallenges = async (level) => { if (hasChallenges) return; const prompt = `Create 30 daily fitness challenge entries for a 30-day group challenge. Return a concise JSON array. Each item must include day (1-30), title, emoji, category, and three personalized variations for beginner, intermediate, and advanced fitness levels. Make the challenges safe, motivating, and varied. Focus on bodyweight, mobility, core, cardio, strength, and recovery.`; const result = await generateAI(prompt); let parsed = []; try { const jsonMatch = String(result || '').match(/\[[\s\S]*\]/); parsed = JSON.parse(jsonMatch ? jsonMatch[0] : result); } catch (e) { parsed = []; } const fallback = Array.from({ length: 30 }, (_, index) => ({ day: index + 1, title: `Daily challenge ${index + 1}`, emoji: 'π₯', category: 'core', beginner: '10 min easy movement', intermediate: '15 min mixed workout', advanced: '25 min performance workout', })); const normalized = (Array.isArray(parsed) && parsed.length ? parsed : fallback).slice(0, 30).map((item, index) => ({ day: item.day || index + 1, title: item.title || `Daily challenge ${index + 1}`, emoji: item.emoji || 'π₯', category: item.category || 'core', fitnessLevel: level, beginner: item.beginner || item.variations?.beginner || '10 min easy movement', intermediate: item.intermediate || item.variations?.intermediate || '15 min mixed workout', advanced: item.advanced || item.variations?.advanced || '25 min performance workout', variation: level, aiGenerated: true, updatedAt: Date.now(), })); for (const challenge of normalized) { await addChallenge(challenge); } }; useEffect(() => { if (!user || challengesLoading || settingsLoading) return; if (!hasChallenges) { ensureDailyChallenges(selectedLevel); } }, [user, challengesLoading, settingsLoading, hasChallenges, selectedLevel]); const buildCertificateHTML = () => `
This certifies that ${user?.name || 'Participant'} has successfully completed all 30 days of the fitness challenge.
Attached is your PDF certificate of completion: ${filename || '30-day-challenge-certificate.pdf'}.
`, attachments: certificateRef.current ? [{ name: filename || 'certificate.pdf', node: certificateRef.current }] : undefined, }); setCertificateStatus('success'); setCertificateMessage('Certificate emailed successfully.'); } catch (error) { setCertificateStatus('error'); setCertificateMessage('Could not email the certificate right now.'); } }; // Real-time presence for authenticated users only. // This broadcasts the current participant identity so todayβs challenge can show active users. const presencePayload = user ? { id: user.id, name: user.name, email: user.email, avatar: user.avatar, tab, activity: 'challenge', fitnessLevel: selectedLevel, updatedAt: Date.now(), } : null; usePresence(presencePayload); if (loading) { return (Warming up...
Sign in with Google to join the group
Your completed days, presence, reactions, and fitness level are tied to your signed-in participant identity.
You'll be redirected to Deplixo login for Google OAuth sign-in.
Welcome, {user.name} β’ Level: {selectedLevel}
Download and email your completion certificate once you finish all 30 days.
{certificateMessage}
: null}