// src/renderer/components/LicenseModal.tsx // Full-screen license management modal with instrument aesthetic import { useState, useEffect, useCallback } from 'react' import { Dialog, DialogTitle, DialogContent, Box, IconButton, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, } from '@mui/material' import CloseIcon from '@mui/icons-material/Close' import CheckCircleIcon from '@mui/icons-material/CheckCircle' import CancelIcon from '@mui/icons-material/Cancel' import { MetalCard, PhosphorText, Led, PhysicalButton } from '@d3ro/ui/components/ds' import { d3roPalette, d3roFontMono, d3roTypo, d3roRadius, d3roShadow } from '@d3ro/ui/theme' import { useI18n } from '@d3ro/i18n' import type { LicenseInfo, LicenseTier, TierComparison, UsageQuota } from '@d3ro/core/types' interface LicenseModalProps { open: boolean onClose: () => void } type LedColor = 'amber' | 'green' | 'red' | 'orange' | 'off' function tierToLedColor(tier: LicenseTier): LedColor { switch (tier) { case 'free': return 'amber' case 'pro': return 'green' case 'pro_plus': return 'green' } } function tierToLabel(tier: LicenseTier, t: (k: string) => string): string { switch (tier) { case 'free': return t('license.free') case 'pro': return t('license.pro') case 'pro_plus': return t('license.proPlus') } } export function LicenseModal({ open, onClose }: LicenseModalProps): React.ReactElement { const { t } = useI18n() const [licenseInfo, setLicenseInfo] = useState(null) const [tierComparison, setTierComparison] = useState([]) const [usageQuotas, setUsageQuotas] = useState([]) const loadData = useCallback(() => { window.electronAPI.license.getInfo().then((r) => { if (r.success) setLicenseInfo(r.data) }) window.electronAPI.license.getTierComparison().then((r) => { if (r.success) setTierComparison(r.data) }) window.electronAPI.license.getAllUsage().then((r) => { if (r.success) setUsageQuotas(r.data) }) }, []) useEffect(() => { if (open) { loadData() } }, [open, loadData]) // Subscribe to tier changes useEffect(() => { const unsub = window.electronAPI.license.onTierChanged((info) => { setLicenseInfo(info) loadData() }) return unsub }, [loadData]) const currentTier = licenseInfo?.tier ?? 'free' const isFree = currentTier === 'free' const isPro = currentTier === 'pro' const handleUpgrade = useCallback(() => { alert(t('license.paymentPending')) }, [t]) return ( {t('license.title')} {/* ---- Current Tier ---- */} {t('license.currentTier')} {licenseInfo ? tierToLabel(licenseInfo.tier, t) : '...'} {/* ---- Subscription Management ---- */} {t('license.subscribe')} {isFree && ( <> {t('license.upgrade')} — {t('license.proPlan')} {t('license.upgrade')} — {t('license.proPlusPlan')} )} {isPro && ( <> {t('license.currentPlan')} — {t('license.proPlan')} {t('license.upgrade')} — {t('license.proPlusPlan')} )} {currentTier === 'pro_plus' && ( {t('license.currentPlan')} — {t('license.proPlusPlan')} )} {/* ---- Daily Usage ---- */} {usageQuotas.length > 0 && ( {t('license.dailyUsage')} {usageQuotas.map((q) => ( {t(`license.feature.${q.feature}` as Parameters[0])} = q.limit ? d3roPalette.tag.red : d3roPalette.accent.amber, width: q.limit < 0 ? '100%' : `${Math.min(100, (q.used / q.limit) * 100)}%`, transition: 'width 0.3s ease', }} /> {q.limit < 0 ? t('license.quotaUnlimited') : t('license.quotaUsed', { used: String(q.used), limit: String(q.limit) })} ))} )} {/* ---- Tier Comparison ---- */} {tierComparison.length > 0 && ( {t('license.tierComparison')}   {t('license.free')} {t('license.pro')} {t('license.proPlus')} {tierComparison.map((row) => ( {t(row.featureLabel as Parameters[0])} {renderTierCell(row.free)} {renderTierCell(row.pro)} {renderTierCell(row.proPlus)} ))}
)}
) } function renderTierCell(value: boolean | string): React.ReactElement { if (typeof value === 'boolean') { return value ? ( ) : ( ) } return ( {value} ) }