// src/renderer/components/LicenseTab.tsx // 구독 기반 라이선스 탭 — 클라우드 인증 + Payple 결제 연동 import { Box, Divider, LinearProgress, } from '@mui/material' import CheckCircleIcon from '@mui/icons-material/CheckCircle' import CancelIcon from '@mui/icons-material/Cancel' import { Led, PhosphorText, PhysicalButton, MetalCard } from '@d3ro/ui/components/ds' import { d3roPalette, d3roFontMono, d3roTypo, d3roRadius } from '@d3ro/ui/theme' import { useI18n } from '@d3ro/i18n' import { PREMIUM_MODEL_LIMITS } from '@d3ro/core/constants' import { useLicenseState } from '../hooks/useLicenseState' export function LicenseTab(): React.ReactElement { const { t } = useI18n() const { licenseInfo, usage, comparison, cloud, currentTier, isFree, isPro, handleUpgrade, handleOpenBilling, openCloudSettings, } = useLicenseState() return ( {/* ── 현재 플랜 + 계정 상태 ── */} {t('license.currentTier')} {cloud.authenticated && cloud.userEmail && ( {cloud.userEmail} )} {/* ── 구독 관리 / 업그레이드 ── */} {/* ── 일일 사용량 ── */} {/* ── 티어 비교표 ── */} ) } // ── 하위 컴포넌트 ────────────────────────────────────── function TierLabel({ tier, t }: { tier: string; t: (k: string) => string }): React.ReactElement { const color = tier === 'pro_plus' ? d3roPalette.tag.purple : tier === 'pro' ? d3roPalette.tag.green : d3roPalette.accent.amber const label = tier === 'pro_plus' ? t('license.proPlus') : tier === 'pro' ? t('license.pro') : t('license.free') return {label} } interface SubscriptionSectionProps { t: (k: string) => string authenticated: boolean isFree: boolean isPro: boolean currentTier: string onUpgrade: (tier: 'pro' | 'pro_plus') => void onManage: () => void onSignIn: () => void } function SubscriptionSection({ t, authenticated, isFree, isPro, onUpgrade, onManage, onSignIn }: SubscriptionSectionProps): React.ReactElement { if (!authenticated) { return ( {t('license.signInRequired')} {t('license.signInToUpgrade')} ) } if (isFree) { return ( {t('license.subscribe')} {t('license.upgradeToProDesc')} ) } if (isPro) { return ( {t('license.subscriptionManage')} ) } // Pro+ return ( {t('license.subscriptionManage')} ) } function ActivePlanIndicator({ label, color }: { label: string; color: string }): React.ReactElement { return ( {label} ) } function UpgradeButton({ tier, t, onUpgrade }: { tier: 'pro' | 'pro_plus'; t: (k: string) => string; onUpgrade: (t: 'pro' | 'pro_plus') => void }): React.ReactElement { const planLabel = tier === 'pro_plus' ? t('license.proPlusPlan') : t('license.proPlan') return ( onUpgrade(tier)} sx={{ width: '100%' }}> {t('license.upgrade')} — {planLabel} ) } function UsageSection({ t, usage, currentTier }: { t: (k: string) => string; usage: Array<{ feature: string; used: number; limit: number; remaining: number }>; currentTier: string }): React.ReactElement | null { if (usage.length === 0) return null return ( <> {t('license.dailyUsage')} {usage.map((q) => ( ))} {/* Premium 모델별 쿼터 */} {PREMIUM_MODEL_LIMITS[currentTier as keyof typeof PREMIUM_MODEL_LIMITS]?.length > 0 && ( <> {t('license.premiumQuota')} {PREMIUM_MODEL_LIMITS[currentTier as keyof typeof PREMIUM_MODEL_LIMITS].map((m) => ( {t(m.i18nKey)} {m.limit === -1 ? t('license.unlimited') : `${m.limit}/${t(m.period === 'weekly' ? 'license.quotaWeekly' : 'license.quotaDaily')}`} {m.limit > 0 && ( )} ))} )} ) } function QuotaBar({ t, feature, used, limit, remaining }: { t: (k: string, p?: Record) => string; feature: string; used: number; limit: number; remaining: number }): React.ReactElement { return ( {t(`license.feature.${feature}`)} {limit === -1 ? t('license.unlimited') : t('license.quotaUsed', { used: String(used), limit: String(limit) })} {limit > 0 && ( = limit ? d3roPalette.tag.red : d3roPalette.accent.amber, borderRadius: d3roRadius.xs, }, }} /> )} ) } function ComparisonTable({ t, comparison }: { t: (k: string) => string; comparison: Array<{ feature: string; featureLabel: string; free: boolean | string; pro: boolean | string; proPlus: boolean | string }> }): React.ReactElement | null { if (comparison.length === 0) return null return ( <> {t('license.tierComparison')} {''} {t('license.free')} {t('license.pro')} {t('license.proPlus')} {comparison.map((row) => ( {t(row.featureLabel)} ))} ) } function TierCell({ value }: { value: boolean | string }): React.ReactElement { if (value === true) { return } if (value === false) { return } return ( {value} ) }