Move info boxes to model popups
This commit is contained in:
fraxle
2026-06-11 18:12:04 +01:00
parent b41c4bd75c
commit 9a9684ba99
12 changed files with 615 additions and 306 deletions
+123
View File
@@ -0,0 +1,123 @@
// ------------------------------------------------------------------------
// components/RestoreModal.js - "Already subscribed? Restore access" popup.
//
// Replaces the old standalone restore.php page. Collects the subscriber's
// email, POSTs it to restore.php (now a JSON endpoint), and on an active
// subscription flips the app into Pro mode in place - no navigation.
//
// Reuses the .welcome-* overlay/card styling; input/error styling is inline
// to avoid new CSS. All open/close state lives in useAppState.
//
// Props:
// onClose - called when the user dismisses (×, backdrop, or Esc)
// setIsPro - state setter from useAppState; called with true on success
// ------------------------------------------------------------------------
import { h } from '../../vendor/preact.js';
import { useEffect, useState } from '../../vendor/preact-hooks.js';
import htm from '../../vendor/htm.js';
import { Wordmark } from './Wordmark.js';
const html = htm.bind(h);
export function RestoreModal({ onClose, setIsPro }) {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const [busy, setBusy] = useState(false);
// Dismiss on Esc.
useEffect(() => {
const onKey = (e) => { if (e.key === 'Escape') onClose(); };
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [onClose]);
const submit = async (e) => {
e.preventDefault();
if (busy) return;
setError('');
setBusy(true);
try {
const res = await fetch('/restore.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ email }),
});
const data = await res.json();
if (data.active) {
try { localStorage.setItem('sunscope_pro', '1'); } catch (err) { /* ignore */ }
setIsPro(true);
onClose();
return;
}
setError(data.error || 'No active SunScope Extra subscription found for that email.');
} catch (err) {
setError('Something went wrong. Please try again.');
}
setBusy(false);
};
return html`
<div class="welcome-overlay" onClick=${onClose}>
<div class="welcome-card" role="dialog" aria-modal="true" aria-labelledby="restore-title"
onClick=${(e) => e.stopPropagation()} style=${{ maxWidth: '420px' }}>
<button class="welcome-close" aria-label="Close" onClick=${onClose}>×</button>
<div class="welcome-head">
<${Wordmark} />
<div style=${{
fontFamily: 'Manrope, sans-serif',
fontWeight: 600,
fontSize: '11px',
letterSpacing: '0.12em',
textTransform: 'uppercase',
color: '#7a5c2a',
marginTop: '10px',
}}>See the world the way your skin does.</div>
</div>
<h2 class="welcome-title" id="restore-title" style=${{ textAlign: 'center', fontSize: '1.3rem', marginBottom: '8px' }}>
Restore your access
</h2>
<p class="welcome-intro">
Enter the email you subscribed with and we'll unlock${' '}
<strong>SunScope Extra</strong> on this browser.
</p>
<form onSubmit=${submit}>
<input
type="email"
required
autofocus
placeholder="your@email.com"
value=${email}
onInput=${(e) => setEmail(e.target.value)}
style=${{
width: '100%',
padding: '11px 14px',
border: '1.5px solid #c9b08a',
borderRadius: '8px',
fontFamily: 'Manrope, sans-serif',
fontSize: '0.95rem',
background: '#fff',
color: '#1e1208',
marginBottom: '12px',
outline: 'none',
boxSizing: 'border-box',
}}
/>
${error && html`<div style=${{
padding: '10px 14px',
background: '#fdf0e0',
border: '1px solid #c9b08a',
borderLeft: '3px solid #c8922a',
borderRadius: '6px',
fontFamily: 'Manrope, sans-serif',
fontSize: '0.85rem',
color: '#4a3420',
marginBottom: '12px',
}}>${error}</div>`}
<button class="welcome-cta" type="submit" disabled=${busy}>
${busy ? 'Checking…' : 'Restore access →'}
</button>
</form>
</div>
</div>`;
}