"use client" import { useState, useEffect } from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Label } from "@/components/ui/label" import { Switch } from "@/components/ui/switch" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" import { Button } from "@/components/ui/button" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Separator } from "@/components/ui/separator" import { usePreferences } from "@/contexts/preferences-context" import { useAuth } from "@/contexts/auth-context" import { getApiUrl } from "@/lib/api-config" import { UserAvatar } from "@/components/ui/user-avatar" import { User, Palette, Bell, Eye, Shield, Sparkles, Check, ArrowLeft, Lock } from "lucide-react" import Link from "next/link" // Avatar color palette - Jewel Tones with XP unlock tiers const PRESET_COLORS = [ // Starter Tier (0 XP) - Always unlocked { value: "#0F4C81", name: "Sapphire", tier: "Starter", xpRequired: 0 }, { value: "#9B111E", name: "Ruby", tier: "Starter", xpRequired: 0 }, { value: "#50C878", name: "Emerald", tier: "Starter", xpRequired: 0 }, { value: "#9966CC", name: "Amethyst", tier: "Starter", xpRequired: 0 }, // Bronze Tier (100 XP) { value: "#0D98BA", name: "Topaz", tier: "Bronze", xpRequired: 100 }, { value: "#E0115F", name: "Rose Quartz", tier: "Bronze", xpRequired: 100 }, { value: "#082567", name: "Lapis", tier: "Bronze", xpRequired: 100 }, { value: "#FF7518", name: "Carnelian", tier: "Bronze", xpRequired: 100 }, // Silver Tier (500 XP) { value: "#006B3C", name: "Jade", tier: "Silver", xpRequired: 500 }, { value: "#1C1C1C", name: "Onyx", tier: "Silver", xpRequired: 500 }, // Gold Tier (1000 XP) { value: "#E6E200", name: "Citrine", tier: "Gold", xpRequired: 1000 }, { value: "#702963", name: "Garnet", tier: "Gold", xpRequired: 1000 }, ] export default function SettingsPage() { const { preferences, updatePreferences, loading } = usePreferences() const { user, refreshUser } = useAuth() // Profile state const [bio, setBio] = useState("") const [username, setUsername] = useState("") const [location, setLocation] = useState("") const [blueskyHandle, setBlueskyHandle] = useState("") const [mastodonHandle, setMastodonHandle] = useState("") const [instagramHandle, setInstagramHandle] = useState("") const [profileSaving, setProfileSaving] = useState(false) const [profileSaved, setProfileSaved] = useState(false) // Avatar state const [avatarBgColor, setAvatarBgColor] = useState("#0F4C81") const [avatarText, setAvatarText] = useState("") const [avatarSaving, setAvatarSaving] = useState(false) const [avatarSaved, setAvatarSaved] = useState(false) const [avatarError, setAvatarError] = useState("") // Privacy state const [privacySettings, setPrivacySettings] = useState({ profile_public: true, show_attendance_public: true, appear_in_leaderboards: true }) useEffect(() => { if (user) { const extUser = user as any setBio(extUser.bio || "") setUsername(extUser.email?.split('@')[0] || "") setLocation(extUser.location || "") setBlueskyHandle(extUser.bluesky_handle || "") setMastodonHandle(extUser.mastodon_handle || "") setInstagramHandle(extUser.instagram_handle || "") setAvatarBgColor(extUser.avatar_bg_color || "#0F4C81") setAvatarText(extUser.avatar_text || "") setPrivacySettings({ profile_public: extUser.profile_public ?? true, show_attendance_public: extUser.show_attendance_public ?? true, appear_in_leaderboards: extUser.appear_in_leaderboards ?? true }) } }, [user]) const handleSaveProfile = async () => { setProfileSaving(true) setProfileSaved(false) const token = localStorage.getItem("token") try { await fetch(`${getApiUrl()}/users/me`, { method: "PATCH", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` }, body: JSON.stringify({ bio, username, location, bluesky_handle: blueskyHandle, mastodon_handle: mastodonHandle, instagram_handle: instagramHandle }) }) setProfileSaved(true) setTimeout(() => setProfileSaved(false), 2000) } catch (e) { console.error(e) } finally { setProfileSaving(false) } } const handleAvatarTextChange = (value: string) => { const cleaned = value.replace(/[^A-Za-z0-9]/g, '').slice(0, 3).toUpperCase() setAvatarText(cleaned) setAvatarError("") } const handleSaveAvatar = async () => { setAvatarSaving(true) setAvatarSaved(false) setAvatarError("") try { const token = localStorage.getItem("token") const res = await fetch(`${getApiUrl()}/users/me/avatar`, { method: "PATCH", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ bg_color: avatarBgColor, text: avatarText || null, }), }) if (!res.ok) { const data = await res.json() throw new Error(data.detail || "Failed to save") } setAvatarSaved(true) refreshUser?.() setTimeout(() => setAvatarSaved(false), 2000) } catch (e: any) { setAvatarError(e.message || "Failed to save avatar") } finally { setAvatarSaving(false) } } const handlePrivacyChange = async (key: string, value: boolean) => { // Optimistic update setPrivacySettings(prev => ({ ...prev, [key]: value })) try { const token = localStorage.getItem("token") await fetch(`${getApiUrl()}/users/me/privacy`, { method: "PATCH", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ [key]: value }), }) } catch (e) { // Revert on error setPrivacySettings(prev => ({ ...prev, [key]: !value })) console.error("Failed to update privacy setting:", e) } } if (loading) { return (
Loading settings...
) } if (!user) { return (

Please Log In

You need to be logged in to access settings.

) } return (
{/* Header */}

Settings

Manage your account and preferences

{/* Desktop: Side-by-side layout, Mobile: Tabs */}
{/* Sidebar Navigation - Sticky */} {/* Settings Content */}
{/* Mobile: Tabs Layout */}
) } // Sidebar Link Component function SidebarLink({ icon: Icon, label, href, active }: { icon: any, label: string, href: string, active?: boolean }) { return ( {label} ) } // Profile Section function ProfileSection({ bio, setBio, username, setUsername, location, setLocation, blueskyHandle, setBlueskyHandle, mastodonHandle, setMastodonHandle, instagramHandle, setInstagramHandle, saving, saved, onSave }: any) { return ( Profile Your public profile information visible to other users
setUsername(e.target.value)} />

This will be shown on your profile and comments

setLocation(e.target.value)} />

Your hometown or local music scene