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 { BrandingSettings } from '@/components/settings/BrandingSettings' 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 user = useAuthStore((s) => s.user) 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) const [inviteError, setInviteError] = useState(null) const [inviteSuccess, setInviteSuccess] = useState(null) 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) } catch (err) { 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) setInviteError(null) setInviteSuccess(null) try { await accountsApi.createInvite({ email: inviteEmail.trim(), role: inviteRole }) setInviteSuccess(`Invitation sent to ${inviteEmail}`) setInviteEmail('') // Refresh invites list const invitesData = await accountsApi.getInvites() setInvites(invitesData) } catch (err) { setInviteError('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)) } catch (err) { 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-[#1e2130] bg-[#14161d] px-3 py-2', 'text-[#e2e5eb] placeholder:text-[#848b9b]', '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-[#1e2130] bg-[#14161d] px-3 py-2', 'text-[#e2e5eb] placeholder:text-[#848b9b]', 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20' )} />
{inviteError && (

{inviteError}

)} {inviteSuccess && (

{inviteSuccess}

)}
{/* 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 Categories Link (owners only) */} {isAccountOwner && (

Team Categories

Manage tree categories for your team

)} {/* Target Lists Link (owners only) */} {isAccountOwner && (

Target Lists

Saved server lists for maintenance flow batch launching

)} {/* Chat Retention Link (owners only) */} {isAccountOwner && (

Chat Retention

Configure AI assistant conversation retention policies

)} {/* Integrations Link (owners only) */} {isAccountOwner && (

Integrations

Connect your PSA to sync session documentation to tickets

)} {/* Branding Link (owners only) */} {isAccountOwner && (

Branding

Customize logo, accent color, and company name

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

Send Feedback

Report bugs, request features, or share your thoughts

{/* Branding Section (owners only) */} {isAccountOwner && user?.team_id && ( )} {/* Preferences Section */}

Preferences

This format will be pre-selected when exporting sessions

{/* SSO Section (Task 11) */} {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