-
-
- {hasPermission(currentUser, 'READ_USERS') &&
-
-
-
-
- Users
-
-
- {users}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_ROLES') &&
-
-
-
-
- Roles
-
-
- {roles}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_PERMISSIONS') &&
-
-
-
-
- Permissions
-
-
- {permissions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_USER_PHYSICAL_BASELINES') &&
-
-
-
-
- User physical baselines
-
-
- {user_physical_baselines}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_USER_LIFESTYLE_BASELINES') &&
-
-
-
-
- User lifestyle baselines
-
-
- {user_lifestyle_baselines}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_GOALS') &&
-
-
-
-
- Goals
-
-
- {goals}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_CONDITIONS') &&
-
-
-
-
- Conditions
-
-
- {conditions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SURGERIES') &&
-
-
-
-
- Surgeries
-
-
- {surgeries}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_IMPLANTS_DEVICES') &&
-
-
-
-
- Implants devices
-
-
- {implants_devices}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_ALLERGIES') &&
-
-
-
-
- Allergies
-
-
- {allergies}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_FAMILY_HISTORIES') &&
-
-
-
-
- Family histories
-
-
- {family_histories}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_HEALTHCARE_PROVIDERS') &&
-
-
-
-
- Healthcare providers
-
-
- {healthcare_providers}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_PHARMACIES') &&
-
-
-
-
- Pharmacies
-
-
- {pharmacies}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MEDICATIONS') &&
-
-
-
-
- Medications
-
-
- {medications}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MEDICATION_DOSES') &&
-
-
-
-
- Medication doses
-
-
- {medication_doses}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MEDICATION_SIDE_EFFECTS') &&
-
-
-
-
- Medication side effects
-
-
- {medication_side_effects}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SUPPLEMENTS') &&
-
-
-
-
- Supplements
-
-
- {supplements}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SUPPLEMENT_EVIDENCE_ITEMS') &&
-
-
-
-
- Supplement evidence items
-
-
- {supplement_evidence_items}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_WORKOUTS') &&
-
-
-
-
- Workouts
-
-
- {workouts}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_EXERCISES') &&
-
-
-
-
- Exercises
-
-
- {exercises}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_WORKOUT_EXERCISES') &&
-
-
-
-
- Workout exercises
-
-
- {workout_exercises}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_WORKOUT_SCHEDULES') &&
-
-
-
-
- Workout schedules
-
-
- {workout_schedules}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_TRAINING_BLOCKS') &&
-
-
-
-
- Training blocks
-
-
- {training_blocks}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_WORKOUT_SESSIONS') &&
-
-
-
-
- Workout sessions
-
-
- {workout_sessions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_STRENGTH_SETS') &&
-
-
-
-
- Strength sets
-
-
- {strength_sets}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_CARDIO_SESSIONS') &&
-
-
-
-
- Cardio sessions
-
-
- {cardio_sessions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_RECOVERY_LOGS') &&
-
-
-
-
- Recovery logs
-
-
- {recovery_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_HEAT_COLD_EXPOSURES') &&
-
-
-
-
- Heat cold exposures
-
-
- {heat_cold_exposures}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_FLEXIBILITY_ASSESSMENTS') &&
-
-
-
-
- Flexibility assessments
-
-
- {flexibility_assessments}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_FOODS') &&
-
-
-
-
- Foods
-
-
- {foods}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_RECIPES') &&
-
-
-
-
- Recipes
-
-
- {recipes}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_RECIPE_COLLECTIONS') &&
-
-
-
-
- Recipe collections
-
-
- {recipe_collections}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MEALS') &&
-
-
-
-
- Meals
-
-
- {meals}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MEAL_ITEMS') &&
-
-
-
-
- Meal items
-
-
- {meal_items}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_NUTRITION_TARGETS') &&
-
-
-
-
- Nutrition targets
-
-
- {nutrition_targets}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MEAL_PLANS') &&
-
-
-
-
- Meal plans
-
-
- {meal_plans}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MEAL_PLAN_ITEMS') &&
-
-
-
-
- Meal plan items
-
-
- {meal_plan_items}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_GROCERY_LISTS') &&
-
-
-
-
- Grocery lists
-
-
- {grocery_lists}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_GROCERY_ITEMS') &&
-
-
-
-
- Grocery items
-
-
- {grocery_items}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_HYDRATION_LOGS') &&
-
-
-
-
- Hydration logs
-
-
- {hydration_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_FASTING_SESSIONS') &&
-
-
-
-
- Fasting sessions
-
-
- {fasting_sessions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_KETONE_GLUCOSE_LOGS') &&
-
-
-
-
- Ketone glucose logs
-
-
- {ketone_glucose_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_BOWEL_MOVEMENTS') &&
-
-
-
-
- Bowel movements
-
-
- {bowel_movements}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_GUT_SYMPTOM_LOGS') &&
-
-
-
-
- Gut symptom logs
-
-
- {gut_symptom_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_HEALTH_METRICS') &&
-
-
-
-
- Health metrics
-
-
- {health_metrics}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SYMPTOM_LOGS') &&
-
-
-
-
- Symptom logs
-
-
- {symptom_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MEDICAL_APPOINTMENTS') &&
-
-
-
-
- Medical appointments
-
-
- {medical_appointments}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_LAB_DOCUMENTS') &&
-
-
-
-
- Lab documents
-
-
- {lab_documents}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_VACCINATIONS') &&
-
-
-
-
- Vaccinations
-
-
- {vaccinations}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_AI_CHAT_THREADS') &&
-
-
-
-
- Ai chat threads
-
-
- {ai_chat_threads}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_AI_CHAT_MESSAGES') &&
-
-
-
-
- Ai chat messages
-
-
- {ai_chat_messages}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_AI_GENERATIONS') &&
-
-
-
-
- Ai generations
-
-
- {ai_generations}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_HABITS') &&
-
-
-
-
- Habits
-
-
- {habits}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_HABIT_LOGS') &&
-
-
-
-
- Habit logs
-
-
- {habit_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_HABIT_CHALLENGES') &&
-
-
-
-
- Habit challenges
-
-
- {habit_challenges}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_LIBRARY_ARTICLES') &&
-
-
-
-
- Library articles
-
-
- {library_articles}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SAVED_ARTICLES') &&
-
-
-
-
- Saved articles
-
-
- {saved_articles}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_GLOSSARY_TERMS') &&
-
-
-
-
- Glossary terms
-
-
- {glossary_terms}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_HEALTH_VAULT_DOCUMENTS') &&
-
-
-
-
- Health vault documents
-
-
- {health_vault_documents}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_EMERGENCY_MEDICAL_CARDS') &&
-
-
-
-
- Emergency medical cards
-
-
- {emergency_medical_cards}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_ADVANCE_DIRECTIVES') &&
-
-
-
-
- Advance directives
-
-
- {advance_directives}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_INSURANCE_POLICIES') &&
-
-
-
-
- Insurance policies
-
-
- {insurance_policies}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_FAMILY_GROUPS') &&
-
-
-
-
- Family groups
-
-
- {family_groups}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_FAMILY_MEMBERS') &&
-
-
-
-
- Family members
-
-
- {family_members}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SHARING_PERMISSIONS') &&
-
-
-
-
- Sharing permissions
-
-
- {sharing_permissions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MOOD_LOGS') &&
-
-
-
-
- Mood logs
-
-
- {mood_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MENTAL_ASSESSMENTS') &&
-
-
-
-
- Mental assessments
-
-
- {mental_assessments}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_JOURNAL_ENTRIES') &&
-
-
-
-
- Journal entries
-
-
- {journal_entries}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_BREATHING_SESSIONS') &&
-
-
-
-
- Breathing sessions
-
-
- {breathing_sessions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_STRESS_LOGS') &&
-
-
-
-
- Stress logs
-
-
- {stress_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_COGNITIVE_TESTS') &&
-
-
-
-
- Cognitive tests
-
-
- {cognitive_tests}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SLEEP_LOGS') &&
-
-
-
-
- Sleep logs
-
-
- {sleep_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SLEEP_ENVIRONMENT_LOGS') &&
-
-
-
-
- Sleep environment logs
-
-
- {sleep_environment_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_NAP_LOGS') &&
-
-
-
-
- Nap logs
-
-
- {nap_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MENSTRUAL_CYCLES') &&
-
-
-
-
- Menstrual cycles
-
-
- {menstrual_cycles}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MENSTRUAL_DAY_LOGS') &&
-
-
-
-
- Menstrual day logs
-
-
- {menstrual_day_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_FERTILITY_LOGS') &&
-
-
-
-
- Fertility logs
-
-
- {fertility_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_PREGNANCIES') &&
-
-
-
-
- Pregnancies
-
-
- {pregnancies}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_PREGNANCY_SYMPTOM_LOGS') &&
-
-
-
-
- Pregnancy symptom logs
-
-
- {pregnancy_symptom_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_BREASTFEEDING_LOGS') &&
-
-
-
-
- Breastfeeding logs
-
-
- {breastfeeding_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_FORMULA_FEEDING_LOGS') &&
-
-
-
-
- Formula feeding logs
-
-
- {formula_feeding_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_BABY_METRICS') &&
-
-
-
-
- Baby metrics
-
-
- {baby_metrics}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_EPDS_ASSESSMENTS') &&
-
-
-
-
- Epds assessments
-
-
- {epds_assessments}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_KICK_COUNTS') &&
-
-
-
-
- Kick counts
-
-
- {kick_counts}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_CONTRACTION_SESSIONS') &&
-
-
-
-
- Contraction sessions
-
-
- {contraction_sessions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_CONTRACTIONS') &&
-
-
-
-
- Contractions
-
-
- {contractions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MENOPAUSE_SYMPTOM_LOGS') &&
-
-
-
-
- Menopause symptom logs
-
-
- {menopause_symptom_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MALE_REPRODUCTIVE_LOGS') &&
-
-
-
-
- Male reproductive logs
-
-
- {male_reproductive_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SEMEN_ANALYSES') &&
-
-
-
-
- Semen analyses
-
-
- {semen_analyses}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_STI_TESTS') &&
-
-
-
-
- Sti tests
-
-
- {sti_tests}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_CONTRACEPTION_LOGS') &&
-
-
-
-
- Contraception logs
-
-
- {contraception_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_LIBIDO_LOGS') &&
-
-
-
-
- Libido logs
-
-
- {libido_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SEXUAL_ACTIVITY_LOGS') &&
-
-
-
-
- Sexual activity logs
-
-
- {sexual_activity_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SKIN_CARE_LOGS') &&
-
-
-
-
- Skin care logs
-
-
- {skin_care_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_SUN_EXPOSURE_LOGS') &&
-
-
-
-
- Sun exposure logs
-
-
- {sun_exposure_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_MOLE_LESION_LOGS') &&
-
-
-
-
- Mole lesion logs
-
-
- {mole_lesion_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_ORAL_HYGIENE_LOGS') &&
-
-
-
-
- Oral hygiene logs
-
-
- {oral_hygiene_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_DENTAL_RECORDS') &&
-
-
-
-
- Dental records
-
-
- {dental_records}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_DENTAL_SYMPTOMS') &&
-
-
-
-
- Dental symptoms
-
-
- {dental_symptoms}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_VISION_PRESCRIPTIONS') &&
-
-
-
-
- Vision prescriptions
-
-
- {vision_prescriptions}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_EYE_SYMPTOM_LOGS') &&
-
-
-
-
- Eye symptom logs
-
-
- {eye_symptom_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_HEARING_TEST_LOGS') &&
-
-
-
-
- Hearing test logs
-
-
- {hearing_test_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_TINNITUS_LOGS') &&
-
-
-
-
- Tinnitus logs
-
-
- {tinnitus_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_NOISE_EXPOSURE_LOGS') &&
-
-
-
-
- Noise exposure logs
-
-
- {noise_exposure_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_PAIN_LOGS') &&
-
-
-
-
- Pain logs
-
-
- {pain_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_INJURIES') &&
-
-
-
-
- Injuries
-
-
- {injuries}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_PHYSIO_EXERCISES') &&
-
-
-
-
- Physio exercises
-
-
- {physio_exercises}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_ERGONOMICS_LOGS') &&
-
-
-
-
- Ergonomics logs
-
-
- {ergonomics_logs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_NOTIFICATIONS') &&
-
-
-
-
- Notifications
-
-
- {notifications}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_EXPORT_JOBS') &&
-
-
-
-
- Export jobs
-
-
- {export_jobs}
-
-
-
-
-
-
-
- }
-
- {hasPermission(currentUser, 'READ_APP_SETTINGS') &&
-
-
-
-
- App settings
-
-
- {app_settings}
-
-
-
-
-
-
-
- }
-
-
+ {errorMessage && (
+
+
+ {errorMessage}
+
+
+ )}
+
+
+
+
+
+
+
+ Today at a glance
+
+
+ Four simple anchors — hydration, mood, sleep, and weight —
+ connected directly to your existing history screens.
+
+
+
+ {loading
+ ? 'Refreshing'
+ : accessibleSummaryCards.length === 0
+ ? 'No daily anchors visible'
+ : `${completedCount} / ${accessibleSummaryCards.length} anchors captured`}
+
+
+
+
+ {summaryCards.map((card) => (
+
+
+
+
+
+
+ {card.isAvailable
+ ? card.isComplete
+ ? 'captured'
+ : 'pending'
+ : 'locked'}
+
+
+
+ {card.label}
+
+
+ {loading ? 'Loading…' : card.value}
+
+
+ {loading ? 'Refreshing this card...' : card.detail}
+
+
+ ))}
+
+
+
+
+
+
+
+ Recent health activity
+
+
+ A real timeline pulled from your existing hydration, mood,
+ sleep, weight, goal, and condition records.
+
+
+
+ Log something new →
+
+
+
+
+ {loading &&
+ Array.from({ length: 4 }).map((_, index) => (
+
+ ))}
+
+ {!loading && dashboardData.recentActivity.length === 0 && (
+
+ There isn't recent activity to show yet. Start with one
+ Daily Pulse check-in or continue onboarding so your home
+ dashboard has something meaningful to reflect.
+
+ )}
+
+ {!loading &&
+ dashboardData.recentActivity.map((item) => {
+ const route = activityRouteByKind[item.kind];
+
+ return (
+
+
+
+
+
+
+ {humanize(item.kind)}
+
+
+
+ {item.title}
+
+
+ {item.detail || 'Open the detail view for more context.'}
+
+
+ {formatDateTime(item.happened_at)}
+
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+
+
+ Current focus
+
+
+ The goal shaping your home dashboard right now.
+
+
+
+
+ {loading ? (
+
+ Loading your current focus...
+
+ ) : !permissions.goalsRead ? (
+
+ Goal visibility is not enabled for your current role.
+
+ ) : dashboardData.latestGoal ? (
+ <>
+
+
+
+ {dashboardData.latestGoal.goal_type
+ ? humanize(dashboardData.latestGoal.goal_type)
+ : 'Primary goal'}
+
+
+ {dashboardData.latestGoal.follow_up_answer
+ ? truncateText(
+ dashboardData.latestGoal.follow_up_answer,
+ 150,
+ )
+ : 'Add a richer follow-up note in the goal vault whenever you want more context here.'}
+
+
+
+ {dashboardData.latestGoal.is_active ? 'active' : 'latest'}
+
+
+
+
+
+
+ Target date
+
+
+ {formatDate(dashboardData.latestGoal.target_date)}
+
+
+
+
+ Active goals total
+
+
+ {dashboardData.activeGoalsCount}
+
+
+
+
+
+ Open goal vault
+
+
+ >
+ ) : (
+
+ No goal has been saved yet. Use onboarding or the goal vault to
+ choose one clear north star for this dashboard.
+
+
+ Choose your first goal →
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ Health baseline snapshot
+
+
+ Your most recent body and lifestyle baselines, surfaced for context.
+
+
+
+
+ {loading ? (
+
+ Loading your baseline snapshot...
+
+ ) : !permissions.physicalRead && !permissions.lifestyleRead ? (
+
+ Baseline visibility is not enabled for your current role.
+
+ ) : baselineHighlights.length === 0 && lifestyleHighlights.length === 0 ? (
+
+ Your body and lifestyle baseline still looks light. Continue
+ onboarding to give future trends and reports stronger context.
+
+ ) : (
+
+ {baselineHighlights.length > 0 && (
+
+
+ Body baseline
+
+
+ {baselineHighlights.map((item) => (
+
+
+ {item.label}
+
+
+ {item.value}
+
+
+ ))}
+
+
+ )}
+
+ {lifestyleHighlights.length > 0 && (
+
+
+ Lifestyle baseline
+
+
+ {lifestyleHighlights.map((item) => (
+
+
+ {item.label}
+
+
+ {item.value}
+
+
+ ))}
+
+
+ )}
+
+
+ Update onboarding and baseline
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ Next best actions
+
+
+ Personalized shortcuts based on what your dashboard is missing.
+
+
+
+
+
+ {nextBestActions.map((action) => (
+
+
+
+
+
+
+
+ {action.title}
+
+
+ {action.description}
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ Care context
+
+
+ The latest condition context currently visible on your account.
+
+
+
+
+ {loading ? (
+
+ Loading your care context...
+
+ ) : !permissions.conditionsRead ? (
+
+ Condition visibility is not enabled for your current role.
+
+ ) : dashboardData.latestCondition ? (
+ <>
+
+
+
+ {dashboardData.latestCondition.condition_name || 'Condition'}
+
+
+ {dashboardData.latestCondition.management_plan
+ ? truncateText(
+ dashboardData.latestCondition.management_plan,
+ 140,
+ )
+ : 'Add a management plan in the conditions module when you want more clinical detail here.'}
+
+
+
+ {dashboardData.latestCondition.status
+ ? humanize(dashboardData.latestCondition.status)
+ : 'Tracked'}
+
+
+
+
+
+
+ Diagnosing doctor
+
+
+ {dashboardData.latestCondition.diagnosing_doctor || 'Not recorded'}
+
+
+
+
+ Last updated
+
+
+ {formatDateTime(dashboardData.latestCondition.updated_at)}
+
+
+
+
+
+ Open conditions
+
+
+ >
+ ) : (
+
+ No conditions are currently tracked on this account. That is
+ completely fine if there is nothing clinically relevant to add.
+
+ )}
+
+
>
- )
+ );
}
-Dashboard.getLayout = function getLayout(page: ReactElement) {
- return
{page}
-}
+DashboardPage.getLayout = function getLayout(page: ReactElement) {
+ return
{page} ;
+};
-export default Dashboard
+export default DashboardPage;
diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx
index 69008d1..ed29694 100644
--- a/frontend/src/pages/index.tsx
+++ b/frontend/src/pages/index.tsx
@@ -1,166 +1,399 @@
-
-import React, { useEffect, useState } from 'react';
-import type { ReactElement } from 'react';
+import {
+ mdiArrowTopRight,
+ mdiCheckCircleOutline,
+ mdiHeartPulse,
+ mdiLeafCircle,
+ mdiTargetVariant,
+ mdiTrophyOutline,
+ mdiWaterOutline,
+ mdiWeatherNight,
+} from '@mdi/js';
import Head from 'next/head';
import Link from 'next/link';
-import BaseButton from '../components/BaseButton';
-import CardBox from '../components/CardBox';
-import SectionFullScreen from '../components/SectionFullScreen';
-import LayoutGuest from '../layouts/Guest';
-import BaseDivider from '../components/BaseDivider';
-import BaseButtons from '../components/BaseButtons';
+import type { ReactElement } from 'react';
+import BaseIcon from '../components/BaseIcon';
import { getPageTitle } from '../config';
-import { useAppSelector } from '../stores/hooks';
-import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
-import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
+import LayoutGuest from '../layouts/Guest';
+const promiseCards = [
+ {
+ title: 'Daily Pulse check-ins',
+ description: 'Save water, mood, sleep, and weight in one elegant flow that already connects to the app’s deeper record screens.',
+ icon: mdiHeartPulse,
+ },
+ {
+ title: 'Premium daily ring',
+ description: 'Get a calm summary of what has been captured today, with a clear score that encourages consistency without overwhelm.',
+ icon: mdiTrophyOutline,
+ },
+ {
+ title: 'Goals in context',
+ description: 'Keep active goals visible beside today’s inputs so actions feel connected to long-term outcomes.',
+ icon: mdiTargetVariant,
+ },
+];
+
+const featureChips = [
+ 'Fitness',
+ 'Nutrition',
+ 'Medical',
+ 'Mental Wellness',
+ 'Sleep',
+ 'Hydration',
+ 'Longevity',
+ 'Family & Teams',
+ 'Health Vault',
+ 'Reports & Exports',
+];
+
+const workflowSteps = [
+ {
+ step: '01',
+ title: 'Capture your essentials',
+ description: 'Log hydration, mood, sleep, and weight from one mobile-first workspace.',
+ icon: mdiWaterOutline,
+ },
+ {
+ step: '02',
+ title: 'See your score instantly',
+ description: 'The Daily Pulse ring turns raw logging into a clear signal for the day.',
+ icon: mdiCheckCircleOutline,
+ },
+ {
+ step: '03',
+ title: 'Drill into full records',
+ description: 'Jump directly into detailed CRUD screens for trends, edits, exports, and history.',
+ icon: mdiArrowTopRight,
+ },
+];
export default function Starter() {
- const [illustrationImage, setIllustrationImage] = useState({
- src: undefined,
- photographer: undefined,
- photographer_url: undefined,
- })
- const [illustrationVideo, setIllustrationVideo] = useState({video_files: []})
- const [contentType, setContentType] = useState('video');
- const [contentPosition, setContentPosition] = useState('right');
- const textColor = useAppSelector((state) => state.style.linkColor);
-
- const title = 'APEX BRAIN'
-
- // Fetch Pexels image/video
- useEffect(() => {
- async function fetchData() {
- const image = await getPexelsImage();
- const video = await getPexelsVideo();
- setIllustrationImage(image);
- setIllustrationVideo(video);
- }
- fetchData();
- }, []);
-
- const imageBlock = (image) => (
-
- );
-
- const videoBlock = (video) => {
- if (video?.video_files?.length > 0) {
- return (
-
-
-
- Your browser does not support the video tag.
-
-
-
)
- }
- };
-
return (
-
+ <>
-
{getPageTitle('Starter Page')}
+
{getPageTitle('APEX BRAIN')}
-
-
- {contentType === 'image' && contentPosition !== 'background'
- ? imageBlock(illustrationImage)
- : null}
- {contentType === 'video' && contentPosition !== 'background'
- ? videoBlock(illustrationVideo)
- : null}
-
-
-
-
-
-
This is a React.js/Node.js app generated by the Flatlogic Web App Generator
-
For guides and documentation please check
- your local README.md and the Flatlogic documentation
+
+
+
+
+
+
+
+
+
+ APEX BRAIN
+
+
+
+
+ Daily Pulse
+
+
+ Onboarding
+
+
+ Dashboard
+
+
+ Login
+
+
+
+
+
+
+
+
+ Your Complete Health Operating System
+
+
+ The warm, premium wellness home for your entire life.
+
+
+ APEX BRAIN brings health, fitness, medical tracking, habits, sleep, hydration, and longevity into one calm, trustworthy experience — with a polished first slice you can use right now.
+
+
+
+
+ Create My Profile
+
+
+ I Already Have an Account
+
+
+ Continue guided onboarding →
+
+
+
+
+
+
+ Aesthetics
+
+
+ Cream backgrounds, forest green accents, and soft gold highlights for a grounded, luxury feel.
+
+
+
+
+ Mobile-first
+
+
+ Designed to feel just as refined on iPhone, Android, and desktop screens.
+
+
+
+
+ First live slice
+
+
+ Daily Pulse is implemented now as the first real workflow, not just a mockup.
+
+
+
+
+
+
+
+
+
+
+
+ Daily Pulse preview
+
+
+ 75 / 100 today
+
+
+
+ live now
+
+
+
+
+
+
+
+ Hydration
+
+
1,250 ml
+
2 entries captured today
+
+
+
+
+ Sleep
+
+
7h 38m
+
Quality 8 / 10
+
+
+
+
+ Mood
+
+
8 / 10
+
Calm • Focused • Optimistic
+
+
+
+
+ Goal focus
+
+
Build better daily habits
+
One ritual connected to the rest of the app.
+
+
+
+
+ Open the Daily Pulse slice
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
© 2026 {title} . All rights reserved
-
- Privacy Policy
-
-
+
+
+
+
+ FIRST DELIVERY
+
+
+ A complete first win, not just a landing page.
+
+
+ The initial APEX BRAIN experience is centered around a premium Daily Pulse workflow: create inputs, see a summary ring, review recent activity, and jump into full detail screens.
+
+
-
+
+ {promiseCards.map((card) => (
+
+
+
+
+
+ {card.title}
+
+
+ {card.description}
+
+
+ ))}
+
+
+
+
+
+
+
+
+ How the slice works
+
+
+ One calm ritual that already feels like a real health product.
+
+
+ Instead of scattering the experience across dozens of unfinished screens, the first release centers attention on the one journey users repeat every day.
+
+
+
+
+ {workflowSteps.map((item) => (
+
+
+
+ {item.step}
+
+
+
+
+
+
+ {item.title}
+
+
+ {item.description}
+
+
+ ))}
+
+
+
+
+
+
+
+
+ What comes next
+
+
+ The foundation is designed to expand into the full APEX BRAIN vision.
+
+
+ The app already has the building blocks for a much broader health platform. Daily Pulse is the first polished slice that can grow into the complete ecosystem below.
+
+
+
+
+ {featureChips.map((chip) => (
+
+ {chip}
+
+ ))}
+
+
+
+
+
+
+ Ready to continue?
+
+
+ Start with the branded slice, then tell us which area should expand next.
+
+
+ You can create an account, start the guided onboarding flow, sign in to the admin interface, or go straight to Daily Pulse once your setup feels right.
+
+
+
+
+ Start onboarding
+
+
+ Open Daily Pulse
+
+
+ Admin Login
+
+
+
+
+
+
+
+
+
+ >
);
}
Starter.getLayout = function getLayout(page: ReactElement) {
return {page} ;
};
-
diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx
index e16bd18..edde696 100644
--- a/frontend/src/pages/login.tsx
+++ b/frontend/src/pages/login.tsx
@@ -21,6 +21,7 @@ import { useAppDispatch, useAppSelector } from '../stores/hooks';
import Link from 'next/link';
import {toast, ToastContainer} from "react-toastify";
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'
+import { hasCompletedApexOnboarding } from '../helpers/apexOnboarding';
export default function Login() {
const router = useRouter();
@@ -64,10 +65,15 @@ export default function Login() {
}, [token, dispatch]);
// Redirect to dashboard if user is logged in
useEffect(() => {
- if (currentUser?.id) {
- router.push('/dashboard');
+ if (!currentUser?.id) {
+ return;
}
- }, [currentUser?.id, router]);
+
+ const shouldStartOnboarding =
+ !hasCompletedApexOnboarding(currentUser.id) && !currentUser.firstName;
+
+ router.push(shouldStartOnboarding ? '/onboarding' : '/dashboard');
+ }, [currentUser?.firstName, currentUser?.id, router]);
// Show error message if there is one
useEffect(() => {
if (errorMessage){
diff --git a/frontend/src/pages/onboarding.tsx b/frontend/src/pages/onboarding.tsx
new file mode 100644
index 0000000..93a74d8
--- /dev/null
+++ b/frontend/src/pages/onboarding.tsx
@@ -0,0 +1,1626 @@
+import {
+ mdiAccount,
+ mdiCheckCircleOutline,
+ mdiChartTimelineVariant,
+ mdiHeartPulse,
+ mdiLeafCircle,
+ mdiTargetVariant,
+} from '@mdi/js';
+import axios from 'axios';
+import Head from 'next/head';
+import Link from 'next/link';
+import React, { useEffect, useMemo, useState } from 'react';
+import type { ReactElement } from 'react';
+import BaseButton from '../components/BaseButton';
+import BaseIcon from '../components/BaseIcon';
+import CardBox from '../components/CardBox';
+import FormField from '../components/FormField';
+import NotificationBar from '../components/NotificationBar';
+import SectionMain from '../components/SectionMain';
+import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
+import { getPageTitle } from '../config';
+import {
+ type ApexOnboardingRecordIds,
+ hasCompletedApexOnboarding,
+ markApexOnboardingCompleted,
+ readApexOnboardingDraft,
+ saveApexOnboardingDraft,
+} from '../helpers/apexOnboarding';
+import { humanize } from '../helpers/humanize';
+import LayoutAuthenticated from '../layouts/Authenticated';
+import { findMe } from '../stores/authSlice';
+import { useAppDispatch, useAppSelector } from '../stores/hooks';
+
+type FeedbackState = {
+ color: 'success' | 'danger';
+ message: string;
+} | null;
+
+type PhysicalRecord = {
+ id: string;
+ height_cm?: number | string | null;
+ weight_kg?: number | string | null;
+ dominant_hand?: string | null;
+ blood_type?: string | null;
+ body_frame_size?: string | null;
+};
+
+type LifestyleRecord = {
+ id: string;
+ typical_bedtime?: string | null;
+ typical_wake_time?: string | null;
+ caffeine_servings_per_day?: number | null;
+ avg_daily_steps_estimate?: number | null;
+ typical_stress_level?: number | null;
+ currently_exercises?: boolean | null;
+ diet_pattern?: string | null;
+ alcohol_use?: string | null;
+ tobacco_nicotine_use?: string | null;
+};
+
+type GoalRecord = {
+ id: string;
+ goal_type?: string | null;
+ target_date?: string | null;
+ follow_up_answer?: string | null;
+};
+
+type ConditionRecord = {
+ id: string;
+ condition_name?: string | null;
+ status?: string | null;
+ diagnosing_doctor?: string | null;
+ management_plan?: string | null;
+};
+
+type OnboardingFormState = {
+ firstName: string;
+ lastName: string;
+ phoneNumber: string;
+ email: string;
+ heightCm: string;
+ weightKg: string;
+ dominantHand: string;
+ bloodType: string;
+ bodyFrameSize: string;
+ typicalBedtime: string;
+ typicalWakeTime: string;
+ caffeineServingsPerDay: string;
+ avgDailyStepsEstimate: string;
+ typicalStressLevel: string;
+ currentlyExercises: string;
+ dietPattern: string;
+ alcoholUse: string;
+ tobaccoNicotineUse: string;
+ goalType: string;
+ goalTargetDate: string;
+ goalFollowUpAnswer: string;
+ conditionName: string;
+ conditionStatus: string;
+ conditionDoctor: string;
+ conditionPlan: string;
+};
+
+type StepDefinition = {
+ key: string;
+ title: string;
+ eyebrow: string;
+ description: string;
+ icon: string;
+};
+
+const palette = {
+ pageBg: '#F5F0E8',
+ panelBg: '#FBF7F0',
+ panelAlt: '#FFF9F0',
+ border: '#E1D6C5',
+ borderStrong: '#D6C8B4',
+ forest: '#4A6741',
+ forestSoft: '#EDF4EA',
+ charcoal: '#1C1C1C',
+ body: '#4C5544',
+ muted: '#7E725E',
+ gold: '#C9A84C',
+ goldSoft: 'rgba(201,168,76,0.16)',
+};
+
+const steps: StepDefinition[] = [
+ {
+ key: 'identity',
+ eyebrow: 'Step 1',
+ title: 'Make APEX BRAIN feel personal',
+ description: 'Set the name and contact details that will appear throughout your home, reports, and profile.',
+ icon: mdiAccount,
+ },
+ {
+ key: 'physical',
+ eyebrow: 'Step 2',
+ title: 'Capture your body baseline',
+ description: 'Start with a few body markers so future trends have context from day one.',
+ icon: mdiHeartPulse,
+ },
+ {
+ key: 'lifestyle',
+ eyebrow: 'Step 3',
+ title: 'Understand your everyday rhythm',
+ description: 'Tell the app how you typically sleep, move, and recover so reminders and reviews feel grounded.',
+ icon: mdiLeafCircle,
+ },
+ {
+ key: 'goal',
+ eyebrow: 'Step 4',
+ title: 'Choose your main health focus',
+ description: 'Pick one primary goal so Daily Pulse and your deeper modules have a clear north star.',
+ icon: mdiTargetVariant,
+ },
+ {
+ key: 'context',
+ eyebrow: 'Step 5',
+ title: 'Add health context if it matters today',
+ description: 'Optional condition notes help your future logs and reports stay clinically useful.',
+ icon: mdiChartTimelineVariant,
+ },
+];
+
+const dominantHandOptions = ['left', 'right', 'ambidextrous'];
+const bloodTypeOptions = ['a_plus', 'a_minus', 'b_plus', 'b_minus', 'ab_plus', 'ab_minus', 'o_plus', 'o_minus', 'unknown'];
+const bodyFrameOptions = ['small', 'medium', 'large', 'unknown'];
+const dietPatternOptions = [
+ 'omnivore',
+ 'vegetarian',
+ 'vegan',
+ 'pescatarian',
+ 'keto',
+ 'paleo',
+ 'mediterranean',
+ 'halal',
+ 'kosher',
+ 'gluten_free',
+ 'dairy_free',
+ 'low_fodmap',
+ 'other',
+];
+const alcoholUseOptions = ['never', 'social', 'weekly', 'daily', 'trying_to_quit'];
+const tobaccoUseOptions = ['never', 'ex_smoker', 'current_smoker', 'vaper', 'chewing_tobacco'];
+const goalTypeOptions = [
+ 'lose_body_fat',
+ 'build_muscle_mass',
+ 'improve_cardiovascular_fitness',
+ 'increase_flexibility_mobility',
+ 'improve_sleep_quality',
+ 'reduce_stress_anxiety',
+ 'manage_chronic_condition_better',
+ 'improve_mental_health',
+ 'optimise_nutrition',
+ 'improve_gut_health',
+ 'improve_hormonal_balance',
+ 'increase_energy_levels',
+ 'longevity_anti_ageing',
+ 'athletic_performance',
+ 'post_surgery_recovery',
+ 'pregnancy_preparation',
+ 'postpartum_recovery',
+ 'improve_sexual_health',
+ 'improve_fertility',
+ 'quit_smoking_or_alcohol',
+ 'build_better_daily_habits',
+ 'improve_cognitive_performance',
+ 'support_family_member_health',
+];
+const conditionStatusOptions = ['active', 'in_remission', 'resolved'];
+
+const moduleLinks = [
+ {
+ href: '/profile',
+ label: 'Profile',
+ description: 'Review your personal identity card and account details.',
+ },
+ {
+ href: '/user_physical_baselines/user_physical_baselines-list',
+ label: 'Body baseline',
+ description: 'See the baseline metrics this setup writes into.',
+ },
+ {
+ href: '/user_lifestyle_baselines/user_lifestyle_baselines-list',
+ label: 'Lifestyle baseline',
+ description: 'Open your everyday sleep, stress, and habit baseline.',
+ },
+ {
+ href: '/goals/goals-list',
+ label: 'Goal vault',
+ description: 'Continue shaping your active goals and target dates.',
+ },
+ {
+ href: '/conditions/conditions-list',
+ label: 'Conditions',
+ description: 'Keep optional condition notes doctor-ready from the start.',
+ },
+ {
+ href: '/daily-pulse',
+ label: 'Daily Pulse',
+ description: 'Use your newly personalized check-in experience.',
+ },
+];
+
+function createEmptyForm(email = ''): OnboardingFormState {
+ return {
+ firstName: '',
+ lastName: '',
+ phoneNumber: '',
+ email,
+ heightCm: '',
+ weightKg: '',
+ dominantHand: '',
+ bloodType: '',
+ bodyFrameSize: '',
+ typicalBedtime: '',
+ typicalWakeTime: '',
+ caffeineServingsPerDay: '',
+ avgDailyStepsEstimate: '',
+ typicalStressLevel: '',
+ currentlyExercises: '',
+ dietPattern: '',
+ alcoholUse: '',
+ tobaccoNicotineUse: '',
+ goalType: '',
+ goalTargetDate: '',
+ goalFollowUpAnswer: '',
+ conditionName: '',
+ conditionStatus: '',
+ conditionDoctor: '',
+ conditionPlan: '',
+ };
+}
+
+function asString(value: unknown) {
+ if (value === null || value === undefined) {
+ return '';
+ }
+
+ return String(value);
+}
+
+function toDateInputValue(value?: string | null) {
+ if (!value) {
+ return '';
+ }
+
+ return value.slice(0, 10);
+}
+
+function toYesNoValue(value?: boolean | null) {
+ if (value === true) {
+ return 'yes';
+ }
+
+ if (value === false) {
+ return 'no';
+ }
+
+ return '';
+}
+
+function trimmedValue(value: string) {
+ return value.trim();
+}
+
+function toNumberOrUndefined(value: string) {
+ if (value.trim() === '') {
+ return undefined;
+ }
+
+ const parsedValue = Number(value);
+ return Number.isNaN(parsedValue) ? undefined : parsedValue;
+}
+
+async function fetchLatestRecordForUser(endpoint: string, userId: string) {
+ const response = await axios.get(
+ `/${endpoint}?page=0&limit=1&sort=desc&field=createdAt&user=${userId}`,
+ );
+
+ if (!Array.isArray(response.data?.rows)) {
+ return null;
+ }
+
+ return (response.data.rows[0] ?? null) as T | null;
+}
+
+async function fetchRecordById(endpoint: string, id?: string) {
+ if (!id) {
+ return null;
+ }
+
+ const response = await axios.get(`/${endpoint}/${id}`);
+ return response.data as T;
+}
+
+function entityHasValues(
+ payload: Record,
+ ignoredKeys: string[] = ['user', 'is_active'],
+) {
+ return Object.entries(payload).some(([key, value]) => {
+ if (ignoredKeys.includes(key)) {
+ return false;
+ }
+
+ return value !== undefined && value !== null && value !== '';
+ });
+}
+
+function FieldError({ error }: { error?: string }) {
+ if (!error) {
+ return null;
+ }
+
+ return {error}
;
+}
+
+export default function OnboardingPage() {
+ const dispatch = useAppDispatch();
+ const { currentUser } = useAppSelector((state) => state.auth);
+ const [activeStep, setActiveStep] = useState(0);
+ const [formState, setFormState] = useState(createEmptyForm());
+ const [recordIds, setRecordIds] = useState({});
+ const [feedback, setFeedback] = useState(null);
+ const [fieldErrors, setFieldErrors] = useState<
+ Partial>
+ >({});
+ const [loading, setLoading] = useState(true);
+ const [saving, setSaving] = useState(false);
+ const [isReadyForAutosave, setIsReadyForAutosave] = useState(false);
+ const [draftMeta, setDraftMeta] = useState<{
+ lastSavedAt?: string;
+ completedAt?: string;
+ }>({});
+
+ const completionPercent = Math.round(((activeStep + 1) / steps.length) * 100);
+ const hasCompletedOnboarding = Boolean(draftMeta.completedAt) || hasCompletedApexOnboarding(currentUser?.id);
+
+ useEffect(() => {
+ if (!currentUser?.id) {
+ return;
+ }
+
+ let isMounted = true;
+
+ const loadOnboarding = async () => {
+ setLoading(true);
+ setFeedback(null);
+ setIsReadyForAutosave(false);
+
+ const draft = readApexOnboardingDraft(currentUser.id);
+ const draftRecordIds = draft?.recordIds ?? {};
+
+ const defaultState = createEmptyForm(currentUser.email ?? '');
+ defaultState.firstName = currentUser.firstName ?? '';
+ defaultState.lastName = currentUser.lastName ?? '';
+ defaultState.phoneNumber = currentUser.phoneNumber ?? '';
+
+ const loadResults = await Promise.allSettled([
+ draftRecordIds.physicalId
+ ? fetchRecordById('user_physical_baselines', draftRecordIds.physicalId)
+ : fetchLatestRecordForUser('user_physical_baselines', currentUser.id),
+ draftRecordIds.lifestyleId
+ ? fetchRecordById('user_lifestyle_baselines', draftRecordIds.lifestyleId)
+ : fetchLatestRecordForUser('user_lifestyle_baselines', currentUser.id),
+ draftRecordIds.goalId
+ ? fetchRecordById('goals', draftRecordIds.goalId)
+ : Promise.resolve(null),
+ draftRecordIds.conditionId
+ ? fetchRecordById('conditions', draftRecordIds.conditionId)
+ : Promise.resolve(null),
+ ]);
+
+ if (!isMounted) {
+ return;
+ }
+
+ const [physicalResult, lifestyleResult, goalResult, conditionResult] = loadResults;
+ const loadErrors = loadResults.filter((result) => result.status === 'rejected');
+
+ if (loadErrors.length) {
+ loadErrors.forEach((result) => {
+ if (result.status === 'rejected') {
+ console.error('Failed to load an onboarding record:', result.reason);
+ }
+ });
+
+ setFeedback({
+ color: 'danger',
+ message:
+ 'We could not load every saved onboarding detail from the server, but you can still continue editing your setup now.',
+ });
+ }
+
+ const physicalRecord =
+ physicalResult.status === 'fulfilled' ? physicalResult.value : null;
+ const lifestyleRecord =
+ lifestyleResult.status === 'fulfilled' ? lifestyleResult.value : null;
+ const goalRecord = goalResult.status === 'fulfilled' ? goalResult.value : null;
+ const conditionRecord =
+ conditionResult.status === 'fulfilled' ? conditionResult.value : null;
+
+ const nextFormState: OnboardingFormState = {
+ ...defaultState,
+ heightCm: asString(physicalRecord?.height_cm),
+ weightKg: asString(physicalRecord?.weight_kg),
+ dominantHand: physicalRecord?.dominant_hand ?? '',
+ bloodType: physicalRecord?.blood_type ?? '',
+ bodyFrameSize: physicalRecord?.body_frame_size ?? '',
+ typicalBedtime: lifestyleRecord?.typical_bedtime ?? '',
+ typicalWakeTime: lifestyleRecord?.typical_wake_time ?? '',
+ caffeineServingsPerDay: asString(lifestyleRecord?.caffeine_servings_per_day),
+ avgDailyStepsEstimate: asString(lifestyleRecord?.avg_daily_steps_estimate),
+ typicalStressLevel: asString(lifestyleRecord?.typical_stress_level),
+ currentlyExercises: toYesNoValue(lifestyleRecord?.currently_exercises),
+ dietPattern: lifestyleRecord?.diet_pattern ?? '',
+ alcoholUse: lifestyleRecord?.alcohol_use ?? '',
+ tobaccoNicotineUse: lifestyleRecord?.tobacco_nicotine_use ?? '',
+ goalType: goalRecord?.goal_type ?? '',
+ goalTargetDate: toDateInputValue(goalRecord?.target_date),
+ goalFollowUpAnswer: goalRecord?.follow_up_answer ?? '',
+ conditionName: conditionRecord?.condition_name ?? '',
+ conditionStatus: conditionRecord?.status ?? '',
+ conditionDoctor: conditionRecord?.diagnosing_doctor ?? '',
+ conditionPlan: conditionRecord?.management_plan ?? '',
+ ...(draft?.form ?? {}),
+ };
+
+ setFormState(nextFormState);
+ setRecordIds({
+ physicalId: draftRecordIds.physicalId ?? physicalRecord?.id,
+ lifestyleId: draftRecordIds.lifestyleId ?? lifestyleRecord?.id,
+ goalId: draftRecordIds.goalId,
+ conditionId: draftRecordIds.conditionId,
+ });
+ setDraftMeta({
+ lastSavedAt: draft?.lastSavedAt,
+ completedAt: draft?.completedAt,
+ });
+ setLoading(false);
+ setIsReadyForAutosave(true);
+ };
+
+ loadOnboarding().catch((error) => {
+ console.error('Failed to initialize APEX BRAIN onboarding:', error);
+
+ if (!isMounted) {
+ return;
+ }
+
+ setFormState({
+ ...createEmptyForm(currentUser.email ?? ''),
+ firstName: currentUser.firstName ?? '',
+ lastName: currentUser.lastName ?? '',
+ phoneNumber: currentUser.phoneNumber ?? '',
+ });
+ setLoading(false);
+ setIsReadyForAutosave(true);
+ setFeedback({
+ color: 'danger',
+ message:
+ 'We hit a setup issue while opening onboarding. You can still complete the form, and any new save will rebuild your draft cleanly.',
+ });
+ });
+
+ return () => {
+ isMounted = false;
+ };
+ }, [currentUser?.email, currentUser?.firstName, currentUser?.id, currentUser?.lastName, currentUser?.phoneNumber]);
+
+ useEffect(() => {
+ if (!isReadyForAutosave || !currentUser?.id) {
+ return;
+ }
+
+ const timeoutId = window.setTimeout(() => {
+ const timestamp = new Date().toISOString();
+
+ saveApexOnboardingDraft(currentUser.id, {
+ form: formState,
+ recordIds,
+ completedAt: draftMeta.completedAt,
+ lastSavedAt: timestamp,
+ });
+
+ setDraftMeta((previousState) => ({
+ ...previousState,
+ lastSavedAt: timestamp,
+ }));
+ }, 300);
+
+ return () => {
+ window.clearTimeout(timeoutId);
+ };
+ }, [currentUser?.id, draftMeta.completedAt, formState, isReadyForAutosave, recordIds]);
+
+ const filledSections = useMemo(() => {
+ return [
+ [formState.firstName, formState.lastName, formState.phoneNumber].filter(Boolean)
+ .length,
+ [
+ formState.heightCm,
+ formState.weightKg,
+ formState.dominantHand,
+ formState.bloodType,
+ formState.bodyFrameSize,
+ ].filter(Boolean).length,
+ [
+ formState.typicalBedtime,
+ formState.typicalWakeTime,
+ formState.caffeineServingsPerDay,
+ formState.avgDailyStepsEstimate,
+ formState.typicalStressLevel,
+ formState.currentlyExercises,
+ formState.dietPattern,
+ formState.alcoholUse,
+ formState.tobaccoNicotineUse,
+ ].filter(Boolean).length,
+ [formState.goalType, formState.goalTargetDate, formState.goalFollowUpAnswer].filter(Boolean)
+ .length,
+ [
+ formState.conditionName,
+ formState.conditionStatus,
+ formState.conditionDoctor,
+ formState.conditionPlan,
+ ].filter(Boolean).length,
+ ];
+ }, [formState]);
+
+ const setupReadiness = useMemo(() => {
+ const totalImportantFields = 10;
+ const completedImportantFields = [
+ formState.firstName,
+ formState.heightCm,
+ formState.weightKg,
+ formState.typicalBedtime,
+ formState.typicalWakeTime,
+ formState.typicalStressLevel,
+ formState.goalType,
+ formState.goalTargetDate,
+ formState.conditionName,
+ formState.conditionPlan,
+ ].filter(Boolean).length;
+
+ return Math.round((completedImportantFields / totalImportantFields) * 100);
+ }, [formState]);
+
+ const currentStepDefinition = steps[activeStep];
+
+ const handleFieldChange =
+ (field: keyof OnboardingFormState) =>
+ (
+ event: React.ChangeEvent<
+ HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
+ >,
+ ) => {
+ const nextValue = event.target.value;
+
+ setFormState((previousState) => ({
+ ...previousState,
+ [field]: nextValue,
+ }));
+
+ setFieldErrors((previousState) => {
+ if (!previousState[field]) {
+ return previousState;
+ }
+
+ const nextErrors = { ...previousState };
+ delete nextErrors[field];
+ return nextErrors;
+ });
+ };
+
+ const validateStep = (stepIndex: number) => {
+ const nextErrors: Partial> = {};
+
+ if (stepIndex === 0 && !trimmedValue(formState.firstName)) {
+ nextErrors.firstName =
+ 'Please enter the name you want APEX BRAIN to use throughout the app.';
+ }
+
+ if (stepIndex === 3 && !formState.goalType) {
+ nextErrors.goalType =
+ 'Choose one primary focus so your home and Daily Pulse can orient around it.';
+ }
+
+ return nextErrors;
+ };
+
+ const scrollToTop = () => {
+ if (typeof window !== 'undefined') {
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }
+ };
+
+ const handleNextStep = () => {
+ const nextErrors = validateStep(activeStep);
+
+ if (Object.keys(nextErrors).length) {
+ setFieldErrors(nextErrors);
+ return;
+ }
+
+ setFieldErrors({});
+ setActiveStep((previousStep) =>
+ Math.min(previousStep + 1, steps.length - 1),
+ );
+ scrollToTop();
+ };
+
+ const handlePreviousStep = () => {
+ setActiveStep((previousStep) => Math.max(previousStep - 1, 0));
+ scrollToTop();
+ };
+
+ const handleJumpToStep = (stepIndex: number) => {
+ if (stepIndex > activeStep) {
+ return;
+ }
+
+ setActiveStep(stepIndex);
+ scrollToTop();
+ };
+
+ const upsertEntity = async (
+ endpoint: string,
+ payload: Record,
+ existingId?: string,
+ createIfMissing = true,
+ ) => {
+ if (!entityHasValues(payload)) {
+ return existingId;
+ }
+
+ if (existingId) {
+ await axios.put(`/${endpoint}/${existingId}`, {
+ id: existingId,
+ data: payload,
+ });
+ return existingId;
+ }
+
+ if (!createIfMissing) {
+ return undefined;
+ }
+
+ const response = await axios.post(`/${endpoint}`, {
+ data: payload,
+ });
+
+ return response.data?.id as string | undefined;
+ };
+
+ const handleSaveOnboarding = async () => {
+ const nextErrors = validateStep(activeStep);
+
+ if (Object.keys(nextErrors).length) {
+ setFieldErrors(nextErrors);
+ return;
+ }
+
+ if (!currentUser?.id) {
+ setFeedback({
+ color: 'danger',
+ message:
+ 'We could not detect your signed-in account. Please refresh the page and try onboarding again.',
+ });
+ return;
+ }
+
+ setSaving(true);
+ setFeedback(null);
+ setFieldErrors({});
+
+ const nextRecordIds: ApexOnboardingRecordIds = {
+ ...recordIds,
+ };
+
+ try {
+ await axios.put('/auth/profile', {
+ profile: {
+ firstName: trimmedValue(formState.firstName),
+ lastName: trimmedValue(formState.lastName),
+ phoneNumber: trimmedValue(formState.phoneNumber),
+ email: currentUser.email,
+ disabled: Boolean(currentUser.disabled),
+ emailVerified: Boolean(currentUser.emailVerified),
+ provider: currentUser.provider,
+ avatar: currentUser.avatar ?? [],
+ },
+ });
+
+ const physicalPayload: Record = {
+ user: currentUser.id,
+ height_cm: toNumberOrUndefined(formState.heightCm),
+ weight_kg: toNumberOrUndefined(formState.weightKg),
+ dominant_hand: formState.dominantHand || undefined,
+ blood_type: formState.bloodType || undefined,
+ body_frame_size: formState.bodyFrameSize || undefined,
+ };
+
+ const lifestylePayload: Record = {
+ user: currentUser.id,
+ typical_bedtime: trimmedValue(formState.typicalBedtime) || undefined,
+ typical_wake_time: trimmedValue(formState.typicalWakeTime) || undefined,
+ caffeine_servings_per_day: toNumberOrUndefined(
+ formState.caffeineServingsPerDay,
+ ),
+ avg_daily_steps_estimate: toNumberOrUndefined(
+ formState.avgDailyStepsEstimate,
+ ),
+ typical_stress_level: toNumberOrUndefined(formState.typicalStressLevel),
+ currently_exercises:
+ formState.currentlyExercises === ''
+ ? undefined
+ : formState.currentlyExercises === 'yes',
+ diet_pattern: formState.dietPattern || undefined,
+ alcohol_use: formState.alcoholUse || undefined,
+ tobacco_nicotine_use: formState.tobaccoNicotineUse || undefined,
+ };
+
+ const goalPayload: Record = {
+ user: currentUser.id,
+ goal_type: formState.goalType || undefined,
+ target_date: formState.goalTargetDate || undefined,
+ follow_up_answer: trimmedValue(formState.goalFollowUpAnswer) || undefined,
+ is_active: true,
+ };
+
+ const conditionPayload: Record = {
+ user: currentUser.id,
+ condition_name: trimmedValue(formState.conditionName) || undefined,
+ status: formState.conditionStatus || undefined,
+ diagnosing_doctor: trimmedValue(formState.conditionDoctor) || undefined,
+ management_plan: trimmedValue(formState.conditionPlan) || undefined,
+ };
+
+ nextRecordIds.physicalId = await upsertEntity(
+ 'user_physical_baselines',
+ physicalPayload,
+ nextRecordIds.physicalId,
+ );
+ nextRecordIds.lifestyleId = await upsertEntity(
+ 'user_lifestyle_baselines',
+ lifestylePayload,
+ nextRecordIds.lifestyleId,
+ );
+ nextRecordIds.goalId = await upsertEntity(
+ 'goals',
+ goalPayload,
+ nextRecordIds.goalId,
+ );
+ nextRecordIds.conditionId = await upsertEntity(
+ 'conditions',
+ conditionPayload,
+ nextRecordIds.conditionId,
+ );
+
+ await dispatch(findMe());
+
+ const completedAt = new Date().toISOString();
+ markApexOnboardingCompleted(currentUser.id, {
+ form: formState,
+ recordIds: nextRecordIds,
+ completedAt,
+ lastSavedAt: completedAt,
+ });
+
+ setRecordIds(nextRecordIds);
+ setDraftMeta({
+ completedAt,
+ lastSavedAt: completedAt,
+ });
+ setFeedback({
+ color: 'success',
+ message:
+ 'Your APEX BRAIN onboarding is saved. Your profile, baseline, and starting focus are now ready for Daily Pulse and the deeper health modules.',
+ });
+ setActiveStep(steps.length - 1);
+ scrollToTop();
+ } catch (error) {
+ console.error('Failed to save APEX BRAIN onboarding:', error);
+ setFeedback({
+ color: 'danger',
+ message:
+ 'We could not finish saving onboarding. Some earlier sections may already be stored, so please review the linked modules below and try again.',
+ });
+ } finally {
+ setSaving(false);
+ }
+ };
+
+ const renderIdentityStep = () => (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+
+ const renderPhysicalStep = () => (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Select one
+ {dominantHandOptions.map((option) => (
+
+ {humanize(option)}
+
+ ))}
+
+
+
+
+
+ Select one
+ {bloodTypeOptions.map((option) => (
+
+ {humanize(option)}
+
+ ))}
+
+
+
+
+
+ Select one
+ {bodyFrameOptions.map((option) => (
+
+ {humanize(option)}
+
+ ))}
+
+
+
+ >
+ );
+
+ const renderLifestyleStep = () => (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Select one
+ Yes
+ No
+
+
+
+
+
+ Select one
+ {dietPatternOptions.map((option) => (
+
+ {humanize(option)}
+
+ ))}
+
+
+
+
+
+
+
+ Select one
+ {alcoholUseOptions.map((option) => (
+
+ {humanize(option)}
+
+ ))}
+
+
+
+
+
+ Select one
+ {tobaccoUseOptions.map((option) => (
+
+ {humanize(option)}
+
+ ))}
+
+
+
+ >
+ );
+
+ const renderGoalStep = () => (
+ <>
+
+
+ Select one
+ {goalTypeOptions.map((option) => (
+
+ {humanize(option)}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ Why this matters
+
+
+ Your primary focus helps APEX BRAIN move from “generic tracker” to “personal health operating system.” It becomes the lens for your Daily Pulse, reviews, and future recommendations.
+
+
+
+
+
+
+
+ >
+ );
+
+ const renderContextStep = () => (
+ <>
+
+
+
+
+
+
+
+ Select one
+ {conditionStatusOptions.map((option) => (
+
+ {humanize(option)}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+
+ const renderCurrentStep = () => {
+ switch (activeStep) {
+ case 0:
+ return renderIdentityStep();
+ case 1:
+ return renderPhysicalStep();
+ case 2:
+ return renderLifestyleStep();
+ case 3:
+ return renderGoalStep();
+ case 4:
+ default:
+ return renderContextStep();
+ }
+ };
+
+ if (loading || !currentUser?.id) {
+ return (
+ <>
+
+ {getPageTitle('Onboarding')}
+
+
+
+ {''}
+
+
+
+
+ APEX BRAIN
+
+
+ Loading your onboarding workspace…
+
+
+ We’re checking your current account, baseline records, and any saved draft so you can continue where you left off.
+
+
+
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+ {getPageTitle('Onboarding')}
+
+
+
+
+ {''}
+
+
+
+
+
+
+
+ Guided setup for your premium health home
+
+
+
+ Let’s turn the schema into a real personal health experience.
+
+
+ This first onboarding slice fills in the data that already exists in APEX BRAIN today: your profile basics, body baseline, lifestyle baseline, first active goal, and optional condition context.
+
+
+
+
+
+
+ {currentStepDefinition.eyebrow} of {steps.length}
+
+ •
+ {currentStepDefinition.title}
+ {hasCompletedOnboarding && (
+
+ Baseline saved
+
+ )}
+
+
+
+
+
+ Snapshot
+
+
+ {setupReadiness}% ready
+
+
+ You can finish this gradually. APEX BRAIN auto-saves your draft in this browser while you fill things in.
+
+
+
+ {[
+ { label: 'Identity', value: `${filledSections[0]}/3` },
+ { label: 'Body', value: `${filledSections[1]}/5` },
+ { label: 'Lifestyle', value: `${filledSections[2]}/9` },
+ { label: 'Goal', value: `${filledSections[3]}/3` },
+ ].map((item) => (
+
+
+ {item.label}
+
+
+ {item.value}
+
+
+ ))}
+
+
+
+
+
+ {feedback && (
+
+
+ ) : undefined
+ }
+ >
+ {feedback.message}
+
+
+ )}
+
+
+
+
+
+ {steps.map((step, index) => {
+ const isActive = index === activeStep;
+ const isClickable = index <= activeStep;
+
+ return (
+
handleJumpToStep(index)}
+ >
+
+
+
+
+
+
+ {step.eyebrow}
+
+
+ {step.title}
+
+
+
+
+ );
+ })}
+
+
+
+
+
+ {currentStepDefinition.eyebrow}
+
+
+ {currentStepDefinition.title}
+
+
+ {currentStepDefinition.description}
+
+
+
+
+ {renderCurrentStep()}
+
+
+
+
+ {draftMeta.lastSavedAt ? (
+ <>
+ Draft last saved in this browser on{' '}
+
+ {new Date(draftMeta.lastSavedAt).toLocaleString()}
+
+ .
+ >
+ ) : (
+ 'Your onboarding draft will auto-save in this browser as you go.'
+ )}
+
+
+
+ {activeStep > 0 && (
+
+ )}
+
+ {activeStep < steps.length - 1 ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ What this setup powers
+
+
+ Your first real APEX BRAIN home
+
+
+
+
+
+ This onboarding doesn’t live in isolation — it writes into the real app modules that already exist today. That means your first setup can immediately feed Daily Pulse, profile reviews, and the deeper admin screens behind the scenes.
+
+
+
+ {moduleLinks.map((link) => (
+
+
+
+
+ {link.label}
+
+
+ {link.description}
+
+
+
+ →
+
+
+
+ ))}
+
+
+
+
+
+
+
+ Your current focus
+
+
+
+ {trimmedValue(formState.firstName) || 'Your profile'}
+
+
+ {formState.goalType
+ ? `${humanize(formState.goalType)} is currently your headline goal.`
+ : 'Choose a primary focus and it will become the anchor for your early APEX BRAIN experience.'}
+
+
+
+
+
+ Daily rhythm snapshot
+
+
+
+ Sleep window:{' '}
+
+ {formState.typicalBedtime || '--:--'} → {formState.typicalWakeTime || '--:--'}
+
+
+
+ Stress level:{' '}
+
+ {formState.typicalStressLevel || 'Not set'}
+
+
+
+ Movement:{' '}
+
+ {formState.currentlyExercises
+ ? humanize(formState.currentlyExercises)
+ : 'Not set'}
+
+
+
+
+
+
+
+ Why this is a first slice
+
+
+ APEX BRAIN’s schema is already broad, but this onboarding flow is the first consumer-friendly layer on top of it. Next we can expand it into deeper medical history, family setup, reproductive health, care-team collaboration, and more guided dashboards.
+
+
+
+
+
+
+
+ >
+ );
+}
+
+OnboardingPage.getLayout = function getLayout(page: ReactElement) {
+ return {page} ;
+};