import { useEffect, useState } from 'react' import { Link } from 'react-router-dom' import { Building2, Users, Mail, Crown, Loader2, AlertCircle, Check, X, Settings, FolderTree, Server, RefreshCw, MessageSquareText, UserCog, AlertTriangle, Clock, Plug, Palette, ShieldCheck } from 'lucide-react' import { PageMeta } from '@/components/common/PageMeta' import { accountsApi } from '@/api/accounts' import type { Account, AccountMember, AccountInvite } from '@/types' import { TransferOwnershipModal } from '@/components/account/TransferOwnershipModal' import { LeaveAccountModal } from '@/components/account/LeaveAccountModal' import { DeleteAccountModal } from '@/components/account/DeleteAccountModal' import { Button } from '@/components/ui/Button' import { Spinner } from '@/components/common/Spinner' import { cn } from '@/lib/utils' import { usePermissions } from '@/hooks/usePermissions' import { useSubscription } from '@/hooks/useSubscription' import { useAuthStore } from '@/store/authStore' import { useUserPreferencesStore } from '@/store/userPreferencesStore' import { CheckoutButton } from '@/components/subscription/CheckoutButton' import { toast } from '@/lib/toast' export function AccountSettingsPage() { const { isAccountOwner } = usePermissions() const { plan, limits, usage } = useSubscription() const { defaultExportFormat, setDefaultExportFormat } = useUserPreferencesStore() const subscription = useAuthStore((s) => s.subscription) const [account, setAccount] = useState(null) const [members, setMembers] = useState([]) const [invites, setInvites] = useState([]) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) // Account name editing const [isEditingName, setIsEditingName] = useState(false) const [editedName, setEditedName] = useState('') const [isSavingName, setIsSavingName] = useState(false) // Modals const [showTransferModal, setShowTransferModal] = useState(false) const [showLeaveModal, setShowLeaveModal] = useState(false) const [showDeleteModal, setShowDeleteModal] = useState(false) // Invite form const [inviteEmail, setInviteEmail] = useState('') const [inviteRole, setInviteRole] = useState('engineer') const [isInviting, setIsInviting] = useState(false) useEffect(() => { loadData() }, []) const loadData = async () => { setIsLoading(true) setError(null) try { const accountData = await accountsApi.getMyAccount() setAccount(accountData) setEditedName(accountData.name) if (isAccountOwner) { const [membersData, invitesData] = await Promise.all([ accountsApi.getMembers(), accountsApi.getInvites(), ]) setMembers(membersData) setInvites(invitesData) } } catch (err) { setError('Failed to load account information') console.error(err) } finally { setIsLoading(false) } } const handleSaveName = async () => { if (!editedName.trim() || editedName === account?.name) { setIsEditingName(false) return } setIsSavingName(true) try { const updated = await accountsApi.updateMyAccount({ name: editedName.trim() }) setAccount(updated) setIsEditingName(false) toast.success('Account name updated') } catch (err) { toast.error('Failed to update account name') console.error('Failed to update account name:', err) } finally { setIsSavingName(false) } } const handleInvite = async (e: React.FormEvent) => { e.preventDefault() if (!inviteEmail.trim()) return setIsInviting(true) try { await accountsApi.createInvite({ email: inviteEmail.trim(), role: inviteRole }) toast.success(`Invitation sent to ${inviteEmail}`) setInviteEmail('') const invitesData = await accountsApi.getInvites() setInvites(invitesData) } catch (err) { toast.error('Failed to send invitation') console.error(err) } finally { setIsInviting(false) } } const [resendingId, setResendingId] = useState(null) const handleResendInvite = async (inviteId: string) => { setResendingId(inviteId) try { await accountsApi.resendInvite(inviteId) toast.success('Invite resent with a new code') const invitesData = await accountsApi.getInvites() setInvites(invitesData) } catch { toast.error('Failed to resend invite') } finally { setResendingId(null) } } const handleRemoveMember = async (userId: string) => { try { await accountsApi.removeMember(userId) setMembers(members.filter((m) => m.id !== userId)) toast.success('Member removed') } catch (err) { toast.error('Failed to remove member') console.error('Failed to remove member:', err) } } if (isLoading) { return (
) } if (error) { return (
{error}
) } const sub = subscription?.subscription return ( <>

Account Settings

Manage your account, subscription, and team

{/* Account Info Section */}

Account Information

{/* Account Name */}
{isEditingName ? (
setEditedName(e.target.value)} className={cn( 'flex-1 rounded-md border border-border bg-card px-3 py-2', 'text-foreground placeholder:text-muted-foreground', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20' )} autoFocus onKeyDown={(e) => { if (e.key === 'Enter') handleSaveName() if (e.key === 'Escape') { setEditedName(account?.name ?? '') setIsEditingName(false) } }} />
) : (
{account?.name} {isAccountOwner && ( )}
)}
{/* Display Code */}

{account?.display_code}

{/* Subscription Section */}

Subscription

{/* Plan & Status */}
{plan.charAt(0).toUpperCase() + plan.slice(1)} Plan {sub && ( {sub.status.charAt(0).toUpperCase() + sub.status.slice(1).replace('_', ' ')} )}
{sub?.current_period_end && (

Current period ends: {new Date(sub.current_period_end).toLocaleDateString()}

)} {/* Usage Stats */} {limits && usage && (
)} {/* Upgrade buttons */} {plan === 'free' && (
)} {plan === 'pro' && (
)}
{/* Team Members Section (owners only) */} {isAccountOwner && (

Team Members

{members.length === 0 ? (

No team members yet.

) : (
{members.map((member) => (

{member.name}

{member.email}

{member.account_role === 'owner' ? ( owner ) : ( )} {!member.is_active && ( Inactive )} {member.account_role !== 'owner' && ( )}
))}
)}
)} {/* Invite Member Section (owners only) */} {isAccountOwner && (

Invite Member

setInviteEmail(e.target.value)} required className={cn( 'flex-1 rounded-md border border-border bg-card px-3 py-2', 'text-foreground placeholder:text-muted-foreground', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20' )} />
{/* Pending Invites */} {invites.length > 0 && (

Pending Invites

{invites .filter((inv) => !inv.used_at) .map((invite) => (

{invite.email}

{invite.expires_at ? `Expires ${new Date(invite.expires_at).toLocaleDateString()}` : 'No expiration'}

{invite.role}
))}
)}
)} {/* Profile Settings Link */}

Profile Settings

Update your name, email, and personal details

{/* Team Settings section (owners only) */} {isAccountOwner && ( <>

Team Settings

Team Categories

Manage flow categories for your team

Target Lists

Saved server and device lists for your team

Chat Retention

Configure AI assistant conversation retention policies

Integrations

Connect your PSA to sync session documentation to tickets

Branding

Customize logo, accent color, and company name

)} {/* Feedback Link (all users) */}

Send Feedback

Report bugs, request features, or share your thoughts

{/* Preferences Section */}

Preferences

This format will be pre-selected when exporting sessions

{/* SSO Section */} {isAccountOwner && (

Single Sign-On (SSO)

Enterprise

SAML and OIDC single sign-on is available for enterprise plans. Contact us to enable SSO for your organization.

Contact Us
)} {/* Danger Zone */}

Danger Zone

{isAccountOwner ? ( <>

Transfer Ownership

Make another member the account owner

Delete Account

Permanently delete your account and all data

) : (

Leave Account

Leave this account and create a personal one

)}
{/* Modals */} {showTransferModal && ( setShowTransferModal(false)} onTransferred={() => { setShowTransferModal(false); loadData() }} /> )} {showLeaveModal && account && ( setShowLeaveModal(false)} /> )} {showDeleteModal && ( setShowDeleteModal(false)} /> )}
) } /** Small helper component for usage stat display */ function UsageStat({ label, current, max, }: { label: string current: number max: number | null }) { const isUnlimited = max === null const percentage = isUnlimited ? 0 : Math.min((current / max) * 100, 100) const isNearLimit = !isUnlimited && percentage >= 80 const isAtLimit = !isUnlimited && current >= max return (

{label}

{current} {' '}/ {isUnlimited ? 'Unlimited' : max}

{!isUnlimited && (
)}
) } export default AccountSettingsPage