import { useState, useRef, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { ChevronDown, Lock, Settings, X } from "lucide-react"; import { useProfile } from "../../contexts/ProfileContext"; import { useEntitlement } from "../../hooks/useEntitlement"; import { requiredTierFor } from "../../shared/entitlements"; import { isProfileSwitchLocked } from "../../shared/profileGate"; import UpsellGate from "../shared/UpsellGate"; import PinDialog from "./PinDialog"; import ProfileFormModal from "./ProfileFormModal"; import type { Profile } from "../../services/profileService"; export default function ProfileSwitcher() { const { t } = useTranslation(); const { profiles, activeProfile, switchProfile, updateProfile } = useProfile(); // Multi-profile gate (Base+, #300): profiles beyond the active one are // LOCKED for a Free user — non-destructive, switching is blocked but nothing // is removed from profiles.json. No lock while `!ready` (anti-flash). const gate = useEntitlement("multi-profile"); const [open, setOpen] = useState(false); const [pinProfile, setPinProfile] = useState(null); const [showManage, setShowManage] = useState(false); const [showUpsell, setShowUpsell] = useState(false); const ref = useRef(null); // Close on outside click useEffect(() => { function handleClick(e: MouseEvent) { if (ref.current && !ref.current.contains(e.target as Node)) { setOpen(false); } } if (open) document.addEventListener("mousedown", handleClick); return () => document.removeEventListener("mousedown", handleClick); }, [open]); const isLocked = (profile: Profile) => isProfileSwitchLocked(profile.id, activeProfile?.id ?? null, gate); const handleSelect = (profile: Profile) => { setOpen(false); if (profile.id === activeProfile?.id) return; if (isLocked(profile)) { setShowUpsell(true); return; } if (profile.pin_hash) { setPinProfile(profile); } else { switchProfile(profile.id); } }; const handlePinSuccess = async (rehashed?: string | null) => { if (pinProfile) { if (rehashed) { try { await updateProfile(pinProfile.id, { pin_hash: rehashed }); } catch { // Best-effort rehash: don't block profile switch if persistence fails } } switchProfile(pinProfile.id); setPinProfile(null); } }; return ( <>
{open && (
{profiles.map((profile) => { const locked = isLocked(profile); return ( ); })}
)}
{pinProfile && ( setPinProfile(null)} /> )} {showManage && ( setShowManage(false)} /> )} {showUpsell && (
setShowUpsell(false)} />
)} ); }