import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { toast } from '@/components/ui/toast-store'; import { useLicense } from '@/context/LicenseContext'; import { TierBadge } from '@/components/TierBadge'; import { Crown, CheckCircle, Check, XCircle, Clock, ExternalLink, CreditCard, RefreshCw, Zap, Compass, ShipWheel, Loader2, } from 'lucide-react'; import { apiFetch } from '@/lib/api'; function getTierDisplayName(tier?: string, variant?: string | null, status?: string): string { if (tier === 'paid' && variant === 'admiral' && status === 'active') return 'Sencho Admiral'; if (tier === 'paid') return 'Sencho Skipper'; return 'Sencho Community'; } export function LicenseSection() { const { license, isPaid, activate, deactivate } = useLicense(); const [licenseKeyInput, setLicenseKeyInput] = useState(''); const [isActivating, setIsActivating] = useState(false); const [isDeactivating, setIsDeactivating] = useState(false); const [billingLoading, setBillingLoading] = useState(false); const openBillingPortal = async () => { setBillingLoading(true); try { const res = await apiFetch('/license/billing-portal', { localOnly: true }); const data = await res.json().catch(() => ({})); if (res.ok && data.url) { window.open(data.url, '_blank'); return; } toast.error(data?.error || data?.message || data?.data?.error || 'Something went wrong.'); } catch { toast.error('Failed to open billing portal.'); } finally { setBillingLoading(false); } }; const isAdmiral = isPaid && license?.variant === 'admiral' && license?.status === 'active'; const showSkipperCard = !isPaid || license?.status === 'trial'; const showUpgradeCards = !isAdmiral && (showSkipperCard || (license?.variant === 'skipper' && license?.status === 'active')); return (

License

Manage your Sencho license.

{/* Current Tier Display */}
{isPaid ? ( ) : ( )} {getTierDisplayName(license?.tier, license?.variant, license?.status)}
{license?.status === 'trial' && license.trialDaysRemaining !== null && (
Trial: {license.trialDaysRemaining} day{license.trialDaysRemaining !== 1 ? 's' : ''} remaining
)} {license?.status === 'active' && (
{license.customerName && (
Customer {license.customerName}
)} {license.productName && (
Plan {license.productName}
)} {license.maskedKey && (
License Key {license.maskedKey}
)} {(license.isLifetime || license.validUntil) && (
{license.isLifetime ? 'Duration' : 'Renews'} {license.isLifetime ? 'Lifetime' : new Date(license.validUntil!).toLocaleDateString()}
)}
)} {license?.status === 'expired' && (
Your license has expired. Renew to restore paid features.
)} {license?.status === 'disabled' && (
Your license has been disabled. Contact support for assistance.
)}
{/* Manage Subscription (active paid license) */} {license?.status === 'active' && (
{!license.isLifetime && ( )}

Deactivating will revert to Community features.

)} {/* Upgrade Cards: show for community, trial, or active Skipper users */} {showUpgradeCards && (
{/* Skipper Card - only for Community users and trial users */} {showSkipperCard && (
Skipper Popular

Professional tools for solo operators.

    {['Fleet View with drill-down', 'Viewer accounts (1 admin + 3 viewers)', 'Webhooks & stack labels', 'Atomic deployments & backups', 'Auto-update policies'].map((f) => (
  • {f}
  • ))}
)} {/* Admiral Card */}
Admiral

For teams managing shared infrastructure.

    {[ ...(license?.variant === 'skipper' ? ['Everything in Skipper'] : ['Everything in Community']), 'Unlimited accounts & scoped RBAC', ...(license?.variant !== 'skipper' ? ['Fleet View, webhooks & labels', 'Atomic deployments & backups'] : []), 'SSO, audit log & host console', 'API tokens & private registries', 'Scheduled operations', ].map((f) => (
  • {f}
  • ))}
)} {/* License key activation */} {license?.status !== 'active' && (
setLicenseKeyInput(e.target.value)} className="font-mono" />
)}
); }