improve home screen
This commit is contained in:
@ -7,17 +7,43 @@ export default function LicenseDialog() {
|
||||
const [key, setKey] = useState('');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [activating, setActivating] = useState(false);
|
||||
const [confirmedEmail, setConfirmedEmail] = useState<string | null>(null);
|
||||
const [verifying, setVerifying] = useState(false);
|
||||
|
||||
const handleActivate = async () => {
|
||||
if (!key.trim()) return;
|
||||
setActivating(true);
|
||||
setError(null);
|
||||
|
||||
const ok = await activateLicense(key.trim());
|
||||
if (!ok) {
|
||||
// If we already verified and the user confirmed, complete activation
|
||||
if (confirmedEmail) {
|
||||
setActivating(true);
|
||||
const ok = await activateLicense(key.trim());
|
||||
if (!ok) {
|
||||
setError('Invalid license key. Check it was entered correctly.');
|
||||
}
|
||||
setActivating(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Verify the key (don't cache yet) to get the email
|
||||
setVerifying(true);
|
||||
try {
|
||||
const payload = await window.electronAPI?.verifyLicense(key.trim());
|
||||
if (payload?.customer_email) {
|
||||
setConfirmedEmail(payload.customer_email);
|
||||
} else {
|
||||
setError('Invalid license key. Check it was entered correctly.');
|
||||
}
|
||||
} catch {
|
||||
setError('Invalid license key. Check it was entered correctly.');
|
||||
}
|
||||
setActivating(false);
|
||||
setVerifying(false);
|
||||
};
|
||||
|
||||
const handleDeny = () => {
|
||||
setConfirmedEmail(null);
|
||||
setKey('');
|
||||
setError(null);
|
||||
};
|
||||
|
||||
const formatDate = (ts: number) => {
|
||||
@ -58,41 +84,47 @@ export default function LicenseDialog() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{showDialog && (
|
||||
<LicenseActivateDialog
|
||||
onClose={() => setShowDialog(false)}
|
||||
onActivate={handleActivate}
|
||||
keyValue={key}
|
||||
setKeyValue={setKey}
|
||||
error={error}
|
||||
activating={activating}
|
||||
trialEnding={status.days_remaining <= 3}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Expired — show banner + activation dialog (both dismissible)
|
||||
return (
|
||||
<>
|
||||
<ExpiredBanner onActivate={() => setShowDialog(true)} />
|
||||
|
||||
{showDialog && (
|
||||
<LicenseActivateDialog
|
||||
onClose={() => setShowDialog(false)}
|
||||
onClose={() => { setShowDialog(false); handleDeny(); }}
|
||||
onActivate={handleActivate}
|
||||
onDeny={handleDeny}
|
||||
keyValue={key}
|
||||
setKeyValue={setKey}
|
||||
error={error}
|
||||
activating={activating}
|
||||
expired
|
||||
verifying={verifying}
|
||||
confirmedEmail={confirmedEmail}
|
||||
trialEnding={status.days_remaining <= 3}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Expired — show banner + activation dialog (both dismissible)
|
||||
return (
|
||||
<>
|
||||
<ExpiredBanner onActivate={() => setShowDialog(true)} />
|
||||
|
||||
{showDialog && (
|
||||
<LicenseActivateDialog
|
||||
onClose={() => { setShowDialog(false); handleDeny(); }}
|
||||
onActivate={handleActivate}
|
||||
onDeny={handleDeny}
|
||||
keyValue={key}
|
||||
setKeyValue={setKey}
|
||||
error={error}
|
||||
activating={activating}
|
||||
verifying={verifying}
|
||||
confirmedEmail={confirmedEmail}
|
||||
expired
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/** Persistent top banner shown when trial expired — still allows export and loading */
|
||||
function ExpiredBanner({ onActivate }: { onActivate: () => void }) {
|
||||
return (
|
||||
@ -112,22 +144,78 @@ function ExpiredBanner({ onActivate }: { onActivate: () => void }) {
|
||||
function LicenseActivateDialog({
|
||||
onClose,
|
||||
onActivate,
|
||||
onDeny,
|
||||
keyValue,
|
||||
setKeyValue,
|
||||
error,
|
||||
activating,
|
||||
verifying,
|
||||
confirmedEmail,
|
||||
trialEnding,
|
||||
expired,
|
||||
}: {
|
||||
onClose: () => void;
|
||||
onActivate: () => void;
|
||||
onDeny: () => void;
|
||||
keyValue: string;
|
||||
setKeyValue: (v: string) => void;
|
||||
error: string | null;
|
||||
activating: boolean;
|
||||
verifying: boolean;
|
||||
confirmedEmail: string | null;
|
||||
trialEnding?: boolean;
|
||||
expired?: boolean;
|
||||
}) {
|
||||
const isProcessing = activating || verifying;
|
||||
|
||||
if (confirmedEmail) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-[80] flex items-center justify-center bg-black/60 px-4">
|
||||
<div
|
||||
className="w-full max-w-md rounded-xl border border-editor-border bg-editor-bg p-6 space-y-4"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Shield className="w-5 h-5 text-editor-accent" />
|
||||
<h3 className="text-sm font-semibold">Confirm License</h3>
|
||||
</div>
|
||||
|
||||
<div className="p-3 rounded-lg bg-editor-surface border border-editor-border space-y-1">
|
||||
<p className="text-xs text-editor-text-muted">
|
||||
This license key is registered to:
|
||||
</p>
|
||||
<p className="text-sm font-medium text-editor-text">{confirmedEmail}</p>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-editor-text-muted leading-relaxed">
|
||||
License keys are tied to your email. Sharing this key may result in deactivation.
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-end gap-2 pt-1">
|
||||
<button
|
||||
onClick={onDeny}
|
||||
className="px-3 py-1.5 rounded-md text-xs text-editor-text-muted hover:text-editor-text hover:bg-editor-surface"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={onActivate}
|
||||
disabled={activating}
|
||||
className="px-4 py-2 bg-editor-accent hover:bg-editor-accent-hover disabled:opacity-40 rounded-lg text-sm font-medium transition-colors flex items-center gap-2"
|
||||
>
|
||||
{activating ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
<Check className="w-4 h-4" />
|
||||
)}
|
||||
Activate
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[80] flex items-center justify-center bg-black/60 px-4">
|
||||
<div
|
||||
@ -188,15 +276,15 @@ function LicenseActivateDialog({
|
||||
|
||||
<button
|
||||
onClick={onActivate}
|
||||
disabled={activating || !keyValue.trim()}
|
||||
disabled={isProcessing || !keyValue.trim()}
|
||||
className="w-full flex items-center justify-center gap-2 px-4 py-2.5 bg-editor-accent hover:bg-editor-accent-hover disabled:opacity-40 rounded-lg text-sm font-medium transition-colors"
|
||||
>
|
||||
{activating ? (
|
||||
{isProcessing ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
<Check className="w-4 h-4" />
|
||||
<Key className="w-4 h-4" />
|
||||
)}
|
||||
Activate
|
||||
{verifying ? 'Verifying...' : 'Verify Key'}
|
||||
</button>
|
||||
|
||||
<p className="text-[10px] text-editor-text-muted text-center">
|
||||
|
||||
Reference in New Issue
Block a user