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' && (
)}
);
}