flnxi-Wu/components/ui/ActivationModal.tsx
2026-05-25 14:52:24 +08:00

170 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState } from 'react';
import { cn } from '@/lib/ui-styles';
interface ActivationModalProps {
isOpen: boolean;
onClose: () => void;
onSuccess?: () => void;
}
export default function ActivationModal({ isOpen, onClose, onSuccess }: ActivationModalProps) {
const [activationCode, setActivationCode] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
if (!isOpen) return null;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError(null);
const code = activationCode.trim().toUpperCase();
if (!code) {
setError('请输入激活码');
return;
}
// 简单格式验证
if (!code.startsWith('FLNXI-')) {
setError('激活码格式不正确');
return;
}
setLoading(true);
try {
const response = await fetch('/api/license/activate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ activationCode: code }),
});
const data = await response.json();
if (data.success) {
onSuccess?.();
onClose();
// 刷新页面以更新许可证状态
window.location.reload();
} else {
setError(data.error || '激活失败,请检查激活码是否正确');
}
} catch (err) {
setError('网络错误,请稍后重试');
} finally {
setLoading(false);
}
};
const handlePurchase = () => {
// 跳转到官网购买页面
window.open('https://www.flnxi.com/pricing', '_blank');
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* 背景遮罩 */}
<div
className="absolute inset-0 bg-black/45"
onClick={onClose}
/>
{/* 弹窗内容 */}
<div className="relative bg-white dark:bg-slate-800 rounded-xl shadow-lg border border-slate-200 dark:border-slate-700 w-full max-w-md mx-4 overflow-hidden">
{/* 头部 */}
<div className="px-6 py-4 border-b border-slate-200 dark:border-slate-700">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-slate-900 dark:text-white">
激活许可证
</h3>
<button
onClick={onClose}
className="p-1 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors"
>
<svg className="w-5 h-5 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
{/* 内容 */}
<form onSubmit={handleSubmit} className="p-6 space-y-4">
<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
激活码
</label>
<input
type="text"
value={activationCode}
onChange={(e) => setActivationCode(e.target.value.toUpperCase())}
placeholder="FLNXI-PRO-XXXX-XXXX-XXXX-XXXX"
className={cn(
'w-full px-4 py-3 rounded-xl border text-sm font-mono',
'bg-slate-50 dark:bg-slate-900',
'border-slate-200 dark:border-slate-700',
'text-slate-900 dark:text-white',
'placeholder:text-slate-400 dark:placeholder:text-slate-500',
'focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent',
'transition-all'
)}
autoFocus
/>
<p className="mt-2 text-xs text-slate-500 dark:text-slate-400">
激活码格式:FLNXI-PRO-XXXX-XXXX-XXXX-XXXX
</p>
</div>
{error && (
<div className="px-4 py-3 rounded-xl bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800">
<p className="text-sm text-red-600 dark:text-red-400">{error}</p>
</div>
)}
<div className="flex gap-3">
<button
type="submit"
disabled={loading}
className={cn(
'flex-1 px-4 py-3 rounded-xl font-medium text-sm transition-all',
'bg-indigo-600 hover:bg-indigo-700 text-white',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
>
{loading ? (
<span className="flex items-center justify-center gap-2">
<svg className="w-4 h-4 animate-spin" viewBox="0 0 24 24" fill="none">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
激活中...
</span>
) : (
'立即激活'
)}
</button>
</div>
</form>
{/* 底部 */}
<div className="px-6 py-4 bg-slate-50 dark:bg-slate-900/50 border-t border-slate-200 dark:border-slate-700">
<div className="flex items-center justify-between">
<p className="text-sm text-slate-500 dark:text-slate-400">
还没有激活码?
</p>
<button
type="button"
onClick={handlePurchase}
className="text-sm font-medium text-indigo-600 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-300 transition-colors"
>
前往购买 →
</button>
</div>
</div>
</div>
</div>
);
}