import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { KeyRound, CreditCard, Megaphone, ChevronRight, Eye, EyeOff, ShieldAlert, Trash2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { Skeleton } from "@/components/ui/skeleton"; import { Badge } from "@/components/ui/badge"; import { Switch } from "@/components/ui/switch"; import { ScrollArea } from "@/components/ui/scroll-area"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { useAuth } from "@/contexts/AuthContext"; import { useProfile } from "@/contexts/ProfileProvider"; import { useClientTiers } from "@/contexts/ClientTiersProvider"; import { useDateFormat } from "@/hooks/useDateFormat"; import api from "@/utils/api.util"; import { toast } from "sonner"; // ─── Section wrapper ────────────────────────────────────────────────────────── function Section({ icon: Icon, title, description, children }) { return ( {title} {description && {description}} {children} ); } // ─── Security ───────────────────────────────────────────────────────────────── function SecuritySection({ user, logout }) { const navigate = useNavigate(); const isGoogle = user?.reg_type === "google"; const [form, setForm] = useState({ current_password: "", new_password: "", confirm: "" }); const [show, setShow] = useState({ current: false, new: false, confirm: false }); const [loading, setLoading] = useState(false); const toggle = (field) => setShow((p) => ({ ...p, [field]: !p[field] })); const set = (field, val) => setForm((p) => ({ ...p, [field]: val })); const handleSubmit = async (e) => { e.preventDefault(); if (form.new_password !== form.confirm) { toast("New passwords do not match."); return; } if (form.new_password.length < 8) { toast("New password must be at least 8 characters."); return; } setLoading(true); try { await api.post("/auth/change-password", { current_password: form.current_password, new_password: form.new_password, }); toast("Password changed. Logging you out…"); setTimeout(async () => { await logout(); navigate("/login"); }, 1500); } catch (err) { toast(err?.response?.data?.message ?? "Could not change password."); } finally { setLoading(false); } }; if (isGoogle) { return (

Your account uses Google Sign-In. Password management is handled through Google.

); } return (
{[ { key: "current", label: "Current password", field: "current_password" }, { key: "new", label: "New password", field: "new_password" }, { key: "confirm", label: "Confirm new password", field: "confirm" }, ].map(({ key, label, field }) => (
set(field, e.target.value)} className="pr-10" required />
))}
); } // ─── Subscription ───────────────────────────────────────────────────────────── const TIER_COLORS = { free: "bg-muted text-muted-foreground", premium: "bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300", exclusive: "bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400", }; function SubscriptionSection() { const navigate = useNavigate(); const { myTier, tierLoading, getMyTier, payments, paymentsLoading, getMyPayments } = useClientTiers(); const { fmtDate, fmtNumber } = useDateFormat(); useEffect(() => { getMyTier(); getMyPayments(); }, []); const tier = myTier?.tier ?? "free"; const expiresAt = myTier?.expires_at ? fmtDate(myTier.expires_at) : null; return (
{/* Current plan */}

Current plan

{tierLoading ? ( ) : (
{tier}
{expiresAt && ( Expires {expiresAt} )} {tier === "free" && ( )}
)}
{/* Payment history */}

Payment history

{paymentsLoading ? (
{[...Array(3)].map((_, i) => )}
) : payments.length === 0 ? (

No payments yet.

) : (
Date Plan Amount Status
{payments.map((p) => ( ))}
{fmtDate(p.createdAt)} {p.plan?.tier ?? "—"} {p.currency} {fmtNumber(p.amount ?? 0)} {p.status}
)}
); } // ─── Advertisements ─────────────────────────────────────────────────────────── const AD_OPTIONS = [ { key: "show_popup_ads", label: "Popup ads", description: "Show promotional popups when you open pages like the dashboard.", }, { key: "show_other_ads", label: "Other ads", description: "Show banner, hero, and sidebar advertisements across the site.", }, ]; function AdvertisementsSection() { const { profile, getProfile, updateProfile, profileLoading } = useProfile(); const [confirmPopupOff, setConfirmPopupOff] = useState(false); useEffect(() => { getProfile(); }, []); const showPopupAds = profile?.personal_info?.show_popup_ads ?? true; const showOtherAds = profile?.personal_info?.show_other_ads ?? true; // No separate stored flag — "hidden" just means both underlying toggles are off, // so it can never drift out of sync with them. const hideAllAds = !showPopupAds && !showOtherAds; const handleToggle = async (key, value) => { // Turning popup ads off also turns off other ads — confirm first since // it's a bigger change than the switch being flipped suggests. if (key === "show_popup_ads" && value === false) { setConfirmPopupOff(true); return; } const result = await updateProfile({ [key]: value }); if (result?.success) { toast("Preference saved.", { description: "Reload the page for this to take effect." }); } }; const confirmTurnOffPopupAndOther = async () => { setConfirmPopupOff(false); const result = await updateProfile({ show_popup_ads: false, show_other_ads: false }); if (result?.success) { toast("Preference saved.", { description: "Reload the page for this to take effect." }); } }; const handleHideAllToggle = async (hide) => { const result = await updateProfile({ show_popup_ads: !hide, show_other_ads: !hide }); if (result?.success) { toast("Preference saved.", { description: "Reload the page for this to take effect." }); } }; return ( <>

Hide all ads

Turn off every advertisement across the platform, popups included.

{profileLoading ? ( ) : ( )}
{AD_OPTIONS.map((opt, i) => (

{opt.label}

{opt.description}

{profileLoading ? ( ) : ( handleToggle(opt.key, v)} /> )}
{i < AD_OPTIONS.length - 1 && }
))}
Turn off popup ads? This will also turn off Other ads (banner, hero, and sidebar advertisements). You can turn either back on here anytime. Cancel Turn off both ); } // ─── Delete Account ─────────────────────────────────────────────────────────── // Disabled while still in development — keep implemented for when we're ready // to expose self-service account deletion. // // function DeleteAccountSection({ logout }) { // const navigate = useNavigate(); // const [open, setOpen] = useState(false); // const [loading, setLoading] = useState(false); // // const handleDelete = async () => { // setLoading(true); // try { // await api.delete("/client/profile"); // toast("Account deleted. Goodbye!", { // action: { // label: "Close", // onClick: () => {} // } // }); // await logout(); // navigate("/login"); // } catch (err) { // toast(err?.response?.data?.message ?? "Could not delete account.", { // action: { // label: "Close", // onClick: () => {} // } // }); // setLoading(false); // } // }; // // return ( // <> //
//
//

Delete account

//

// Permanently remove your account and all associated data. This action cannot be undone. //

//
// //
// // // // // Delete your account? // // This will permanently delete your account and sign you out of all sessions. // Your data cannot be recovered after deletion. // // // // Cancel // // {loading ? "Deleting…" : "Yes, delete my account"} // // // // // // ); // } // ─── Page ───────────────────────────────────────────────────────────────────── export default function AccountSettings() { const { user, logout } = useAuth(); return (

Account Settings

Manage your security, subscription, and preferences.

{/*
*/}
); }