"use client"; import { useEffect, useState } from "react"; import { LogoutButton } from "@/components/logout-button"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { clearDailyReminderSentToday, deliverTestDailyReminder, getNotificationPermissionLabel, getNotificationPermissionState, isNotificationSupported, requestNotificationPermission, } from "@/lib/daily-reminders"; import { getDailyReminderStatus } from "@/lib/daily-reminder-status"; import { adjustCoins } from "@/lib/avatar-progression-storage"; import { notifyHabitPetDataUpdated } from "@/lib/app-events"; import { DEMO_COINS_GRANT } from "@/lib/coins"; import { resetTodaysDailyQuiz } from "@/lib/daily-quiz-storage"; import { getTodayDateKey } from "@/lib/habits-storage"; import { reminderDeliveryOptions, usesSystemReminders, type ReminderDeliveryMethod, } from "@/lib/reminder-delivery"; import { avatarVibeOptions, defaultProfilePreferences, focusTopicOptions, getProfilePreferences, saveProfilePreferences, type ProfilePreferences, } from "@/lib/profile-preferences-storage"; type ProfilePreferencesFormProps = { email?: string | null; }; export function ProfilePreferencesForm({ email }: ProfilePreferencesFormProps) { const [preferences, setPreferences] = useState( defaultProfilePreferences, ); const [isReady, setIsReady] = useState(false); const [error, setError] = useState(null); const [isSaving, setIsSaving] = useState(false); const [notificationPermission, setNotificationPermission] = useState( getNotificationPermissionState(), ); const [notificationMessage, setNotificationMessage] = useState( null, ); const [quizResetMessage, setQuizResetMessage] = useState(null); const [isResettingQuiz, setIsResettingQuiz] = useState(false); const [coinsGrantMessage, setCoinsGrantMessage] = useState(null); const [isGrantingCoins, setIsGrantingCoins] = useState(false); useEffect(() => { setNotificationPermission(getNotificationPermissionState()); }, []); useEffect(() => { async function loadPreferences() { try { setPreferences(await getProfilePreferences()); } catch (loadError) { setError( loadError instanceof Error ? loadError.message : "Could not load profile preferences.", ); } finally { setIsReady(true); } } void loadPreferences(); }, []); const updatePreferences = async (updates: Partial) => { const nextPreferences = { ...preferences, ...updates }; setPreferences(nextPreferences); setIsSaving(true); setError(null); try { const savedPreferences = await saveProfilePreferences(nextPreferences); setPreferences(savedPreferences); if ( "dailyReminderEnabled" in updates || "dailyReminderTime" in updates || "dailyReminderDelivery" in updates ) { clearDailyReminderSentToday(getTodayDateKey()); } } catch (saveError) { setPreferences(preferences); setError( saveError instanceof Error ? saveError.message : "Could not save profile preferences.", ); } finally { setIsSaving(false); } }; const ensureSystemReminderPermission = async ( deliveryMethod: ReminderDeliveryMethod, ) => { if (!usesSystemReminders(deliveryMethod)) { return true; } if (!isNotificationSupported()) { setError("This browser does not support browser (OS) notifications."); return false; } const permission = await requestNotificationPermission(); setNotificationPermission(permission); if (permission !== "granted") { setError( permission === "denied" ? "Browser notifications are blocked. Enable them in your browser settings or choose In-app delivery." : "Browser notification permission was not granted.", ); return false; } return true; }; const handleDailyReminderToggle = async (checked: boolean) => { if (checked) { const allowed = await ensureSystemReminderPermission( preferences.dailyReminderDelivery, ); if (!allowed) { return; } } setError(null); await updatePreferences({ dailyReminderEnabled: checked }); }; const handleDeliveryMethodChange = async ( deliveryMethod: ReminderDeliveryMethod, ) => { if ( preferences.dailyReminderEnabled && usesSystemReminders(deliveryMethod) ) { const allowed = await ensureSystemReminderPermission(deliveryMethod); if (!allowed) { return; } } setError(null); await updatePreferences({ dailyReminderDelivery: deliveryMethod }); }; const handleTestNotification = async () => { setNotificationMessage(null); setError(null); const deliveryMethod = preferences.dailyReminderDelivery; if (usesSystemReminders(deliveryMethod)) { const allowed = await ensureSystemReminderPermission(deliveryMethod); if (!allowed) { return; } } const status = await getDailyReminderStatus(); const shown = deliverTestDailyReminder(status, deliveryMethod); if (!shown) { setError("Could not show a test reminder."); return; } if (deliveryMethod === "in_app") { setNotificationMessage( "In-app test reminder sent. Look for the banner above the bottom navigation.", ); return; } if (deliveryMethod === "system") { setNotificationMessage( "Browser test notification sent. Check your browser or OS notification center.", ); return; } setNotificationMessage( "Test reminder sent in-app and through your browser notification center.", ); }; const handleGrantDemoCoins = async () => { setCoinsGrantMessage(null); setError(null); setIsGrantingCoins(true); try { const nextBalance = await adjustCoins(DEMO_COINS_GRANT); notifyHabitPetDataUpdated(); setCoinsGrantMessage( `Added ${DEMO_COINS_GRANT} points. Your balance is now ${nextBalance}.`, ); } catch (grantError) { setError( grantError instanceof Error ? grantError.message : "Could not add demo points.", ); } finally { setIsGrantingCoins(false); } }; const handleResetDailyQuiz = async () => { setQuizResetMessage(null); setError(null); setIsResettingQuiz(true); try { await resetTodaysDailyQuiz(); setQuizResetMessage( "Today's quiz was reset. Open the Quiz tab to take it again.", ); } catch (resetError) { setError( resetError instanceof Error ? resetError.message : "Could not reset today's quiz.", ); } finally { setIsResettingQuiz(false); } }; if (!isReady) { return (

Loading profile...

); } return (
{error ?

{error}

: null} Profile information Your account details.
Focus topic preference Choose the one area you want your bit and daily tasks to focus on. {focusTopicOptions.map((topic) => ( ))} Avatar preferences Personalize your bit's vibe. {avatarVibeOptions.map((vibe) => ( ))} Notification preferences Control reminders for your daily quiz and habits.
void handleDailyReminderToggle(checked === true) } />
{reminderDeliveryOptions.map((option) => ( ))}
{usesSystemReminders(preferences.dailyReminderDelivery) ? (
Browser permission {getNotificationPermissionLabel(notificationPermission)}
) : null}
void updatePreferences({ dailyReminderTime: event.target.value }) } />

In-app reminders appear inside HaBit while a tab is open. Browser (OS) reminders use the Web Notifications API and may appear in your system notification center. Reminders fire once per day at your chosen local time.

{notificationMessage ? (

{notificationMessage}

) : null}
Demo tools Shortcuts for testing flows without waiting for the daily reset.

Reset clears today's wellness quiz entry so you can complete it again immediately. Demo points are for testing the shop only.

{coinsGrantMessage ? (

{coinsGrantMessage}

) : null} {quizResetMessage ? (

{quizResetMessage}

) : null}
Account settings Session and onboarding status.
Onboarding Complete
); }