import { useEffect, useRef, useState } from "react"; import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { ArrowLeft, CreditCard, Mail, Shield, Users, X } from "lucide-react"; import { toast } from "sonner"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Separator } from "@/components/ui/separator"; import { Muted } from "@/components/ui/typography"; import { WindowControls } from "@/components/window-controls"; import { useNetworks } from "@/hooks/use-networks"; import { useNetworkInvitations, useInviteMembers, useRevokeInvitation, useRemoveMember, } from "@/hooks/use-member-management"; import { useAuthStore } from "@/stores/auth-store"; import { BillingSection } from "@/features/network-billing"; import { ConfirmDestructiveOverlay } from "@/components/confirm-destructive-overlay"; import type { Human } from "@/api/types"; function MemberRow({ human, isAdmin, onRemove, }: { human: Human; isAdmin: boolean; onRemove?: () => void; }) { const initials = human.email_prefix.slice(0, 2).toUpperCase(); return (
{initials}

{human.email_prefix}

{human.email}
{isAdmin && ( Admin )} {onRemove && ( )}
); } function InviteForm({ networkId }: { networkId: string }) { const [email, setEmail] = useState(""); const inviteMembers = useInviteMembers(networkId); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); const trimmed = email.trim(); if (!trimmed) return; inviteMembers.mutate([trimmed], { onSuccess: () => { toast.success(`Invitation sent to ${trimmed}`); setEmail(""); }, }); }; return (
setEmail(e.target.value)} className="flex-1" />
); } function PendingInvitationRow({ email, networkId, }: { email: string; networkId: string; }) { const revokeInvitation = useRevokeInvitation(networkId); const handleRevoke = () => { revokeInvitation.mutate(email, { onSuccess: () => { toast.success(`Invitation to ${email} revoked`); }, }); }; return (

{email}

Pending
); } function SectionHeader({ icon, title, description, trailing, }: { icon: React.ReactNode; title: string; description?: string; trailing?: React.ReactNode; }) { return (
{icon}

{title}

{trailing}
{description && {description}}
); } function Section({ children }: { children: React.ReactNode }) { return (
{children}
); } export default function NetworkSettingsPage() { const navigate = useNavigate(); const { networkId } = useParams<{ networkId: string }>(); const [searchParams] = useSearchParams(); const { data: networks } = useNetworks(); const network = networks?.find((n) => n.id === networkId); const { data: invitations, error: invitationsError } = useNetworkInvitations(networkId!); const currentUser = useAuthStore((s) => s.user); const isAdmin = currentUser?.id === network?.admin_human.id; const [memberToRemove, setMemberToRemove] = useState(null); const removeMember = useRemoveMember(networkId!); const billingRef = useRef(null); useEffect(() => { if (searchParams.get("section") === "billing") { billingRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }); } }, [searchParams]); const networkName = network?.name ?? "Network"; const memberCount = network?.humans.length ?? 0; const pendingCount = invitations?.length ?? 0; const networkInitials = networkName.slice(0, 2).toUpperCase(); return (
Settings
{networkInitials}

{networkName}

{memberCount} {memberCount === 1 ? "member" : "members"} {isAdmin ? " · You're an admin" : ""}
} title="Members" description="People with access to this network." trailing={ {memberCount} } /> {network?.humans.map((human, index) => { const isRowAdmin = human.id === network.admin_human.id; const canRemove = isAdmin && !isRowAdmin && human.id !== currentUser?.id; return (
setMemberToRemove(human) : undefined} /> {index < network.humans.length - 1 && ( )}
); })}
{isAdmin && network && (
} title="Invitations" description="Invite teammates by email. They'll get a link to join." trailing={ pendingCount > 0 ? ( {pendingCount} pending ) : undefined } /> {invitationsError && ( <>

Couldn't load pending invitations.

)} {pendingCount > 0 && ( <>
Pending
{invitations!.map((inv, index) => (
{index < invitations!.length - 1 && ( )}
))} )}
)}
} title="Billing" description={ isAdmin ? "Manage your plan, seats, and payment." : "Your network's current plan and usage." } />
{memberToRemove && (
  • They'll lose access to this network's streams and files within seconds.
  • Any content they posted stays in the network.
  • If they're in a live huddle, they may remain until the call ends.
  • } confirmLabel="Remove" pendingLabel="Removing…" isPending={removeMember.isPending} onConfirm={() => { const target = memberToRemove; removeMember.mutate(target.id, { onSuccess: () => { toast.success(`Removed ${target.email}`); setMemberToRemove(null); }, }); }} onClose={() => { if (!removeMember.isPending) setMemberToRemove(null); }} /> )}
    ); }