diff --git a/frontend/src/components/LoginPage.jsx b/frontend/src/components/LoginPage.jsx index 0019854..cab0455 100644 --- a/frontend/src/components/LoginPage.jsx +++ b/frontend/src/components/LoginPage.jsx @@ -1,25 +1,149 @@ import { useState } from 'react' -import { useNavigate } from 'react-router-dom' +import { useNavigate, Link } from 'react-router-dom' import { useAuth } from '../AuthContext' import { BASE_URL } from '../api' -const ACCENT = '#E85A4F' -const ACCENT_HOVER= '#D14940' -const CREAM = '#FFFBF5' -const INK = '#1A1A1A' -const INK_MUTED = '#7A7A7A' -const BORDER = '#E5E0D8' - export default function LoginPage() { const [mode, setMode] = useState('login') // 'login' | 'register' + + return ( +
+ + {/* ── LEFT — dark ink branding panel ─────────────────────── */} +
+ {/* Decorative blobs */} +
+
+ + {/* Logo */} + + Leopost + + + {/* Testimonial */} +
+
+
+ "Finalmente un assistente che capisce il mio brand e mi fa risparmiare ore ogni settimana." +
+
+
+ MR +
+
+

Marco Rossi

+

Social Media Manager

+
+
+
+ + {/* Copyright */} +

+ © 2026 Leopost +

+
+ + {/* ── RIGHT — form panel ──────────────────────────────────── */} +
+ {/* Mobile header */} +
+ + Leopost + +
+ + {/* Form area */} +
+
+ {mode === 'login' ? ( + setMode('register')} /> + ) : ( + setMode('login')} /> + )} +
+
+
+ + {/* CSS for responsive left panel */} + +
+ ) +} + +// ── Login form ───────────────────────────────────────────────────────────────── + +function LoginForm({ onSwitchMode }) { const [email, setEmail] = useState('') - const [displayName, setDisplayName] = useState('') const [password, setPassword] = useState('') const [error, setError] = useState('') const [loading, setLoading] = useState(false) - const [showRedeemModal, setShowRedeemModal] = useState(false) - const { login, register } = useAuth() + const { login } = useAuth() const navigate = useNavigate() const handleSubmit = async (e) => { @@ -27,475 +151,364 @@ export default function LoginPage() { setError('') setLoading(true) try { - if (mode === 'login') { - await login(email, password) - } else { - await register(email, password, displayName) - } + await login(email, password) navigate('/') } catch (err) { - setError(err.message || (mode === 'login' ? 'Credenziali non valide' : 'Errore durante la registrazione')) + setError(err.message || 'Email o password errata') } finally { setLoading(false) } } - const handleGoogleLogin = () => { - window.location.href = `${BASE_URL}/auth/oauth/google` + return ( +
+ {/* Header */} +
+ + Bentornato + +

+ Accedi a Leopost +

+

+ Inserisci le tue credenziali per continuare +

+
+ + {/* Google button */} + { window.location.href = `${BASE_URL}/auth/oauth/google` }} /> + + {/* Divider */} + + + {/* Form */} +
+ {error && } + + + setEmail(e.target.value)} + placeholder="tu@esempio.it" + required + style={inputStyle} + onFocus={(e) => e.target.style.borderColor = '#1A1A1A'} + onBlur={(e) => e.target.style.borderColor = '#E5E0D8'} + /> + + + + setPassword(e.target.value)} + placeholder="La tua password" + required + style={inputStyle} + onFocus={(e) => e.target.style.borderColor = '#1A1A1A'} + onBlur={(e) => e.target.style.borderColor = '#E5E0D8'} + /> + + +
+ + Password dimenticata? + +
+ + + {loading ? 'Accesso in corso...' : 'Accedi'} + + + + {/* Footer */} +
+

+ Non hai un account?{' '} + +

+
+
+ ) +} + +// ── Register form ────────────────────────────────────────────────────────────── + +function RegisterForm({ onSwitchMode }) { + const [email, setEmail] = useState('') + const [displayName, setDisplayName] = useState('') + const [password, setPassword] = useState('') + const [error, setError] = useState('') + const [loading, setLoading] = useState(false) + + const { register } = useAuth() + const navigate = useNavigate() + + const handleSubmit = async (e) => { + e.preventDefault() + setError('') + setLoading(true) + try { + await register(email, password, displayName) + navigate('/') + } catch (err) { + setError(err.message || 'Errore durante la registrazione') + } finally { + setLoading(false) + } } return ( -
- {/* ── LEFT PANEL ─────────────────────────────────────────── */} -
-
- {/* Tag editoriale */} - - LEOPOST - - {/* editorial-line bianco */} -
- -

- Il tuo studio
editoriale AI -

- -

- Genera, schedula e pubblica contenuti su tutti i social — con l'AI al tuo fianco. -

- - {/* Benefit list */} -
    - {[ - 'Personaggi AI con voce e stile unico', - 'Contenuti su Facebook, Instagram, YouTube, TikTok', - 'Schedulazione automatica con piani editoriali', - ].map((txt) => ( -
  • - - {txt} -
  • - ))} -
-
- - {/* Badge in basso */} -
+ {/* Header */} +
+ - EARLY ADOPTER BETA -
+ Inizia gratis + +

+ Crea il tuo account +

+

+ Inizia a gestire i tuoi social in modo intelligente +

- {/* ── RIGHT PANEL ────────────────────────────────────────── */} -
{ window.location.href = `${BASE_URL}/auth/oauth/google` }} /> + + {/* Divider */} + + + {/* Form */} +
+ {error && } + + + setDisplayName(e.target.value)} + placeholder="Il tuo nome o nickname" + style={inputStyle} + onFocus={(e) => e.target.style.borderColor = '#1A1A1A'} + onBlur={(e) => e.target.style.borderColor = '#E5E0D8'} + /> + + + + setEmail(e.target.value)} + placeholder="tu@esempio.it" + required + style={inputStyle} + onFocus={(e) => e.target.style.borderColor = '#1A1A1A'} + onBlur={(e) => e.target.style.borderColor = '#E5E0D8'} + /> + + + + setPassword(e.target.value)} + placeholder="Minimo 8 caratteri" + required + style={inputStyle} + onFocus={(e) => e.target.style.borderColor = '#1A1A1A'} + onBlur={(e) => e.target.style.borderColor = '#E5E0D8'} + /> + + + + {loading ? 'Creazione account...' : 'Crea account'} + + + + {/* Footer */} +
+

+ Hai già un account?{' '} + +

+
+
+ ) +} + +// ── Shared sub-components ────────────────────────────────────────────────────── + +function GoogleButton({ onClick }) { + return ( + + ) +} - {/* ── Mode toggle ── */} -
- {[ - { key: 'login', label: 'Accedi' }, - { key: 'register', label: 'Registrati' }, - ].map(({ key, label }) => ( - - ))} -
- - {/* ── Form ── */} -
- {error && ( -
- {error} -
- )} - - {mode === 'register' && ( -
- - setDisplayName(e.target.value)} - placeholder="Il tuo nome o nickname" - style={inputStyle} - /> -
- )} - -
- - setEmail(e.target.value)} - placeholder="tu@esempio.it" - required - style={inputStyle} - /> -
- -
- - setPassword(e.target.value)} - placeholder="••••••••" - required - style={inputStyle} - /> -
- - -
- - {/* ── Divider ── */} -
-
- oppure -
-
- - {/* ── Google ── */} - - - {/* ── Coming soon ── */} -
- {[ - { name: 'Facebook', icon: '📘' }, - { name: 'Microsoft', icon: '🪟' }, - { name: 'Apple', icon: '🍎' }, - ].map(({ name, icon }) => ( - - ))} -
-

- Altri provider disponibili a breve -

- - {/* ── Redeem link ── */} -

- Hai un codice Pro?{' '} - -

-
+function Divider() { + return ( +
+
+
+
+
+ + oppure +
- - {showRedeemModal && setShowRedeemModal(false)} />}
) } -// ── Redeem modal ─────────────────────────────────────────────────────────────── - -function RedeemModal({ onClose }) { - const [code, setCode] = useState('') - const [loading, setLoading] = useState(false) - const [message, setMessage] = useState('') - const [error, setError] = useState('') - - const handleRedeem = async (e) => { - e.preventDefault() - setLoading(true) - setError('') - setMessage('') - try { - const { api } = await import('../api') - const result = await api.post('/auth/redeem', { code }) - setMessage(`Codice riscattato! Piano Pro attivo fino al ${new Date(result.subscription_expires_at).toLocaleDateString('it-IT')}.`) - } catch (err) { - setError(err.message || 'Codice non valido o già utilizzato.') - } finally { - setLoading(false) - } - } +function Field({ label, children }) { + return ( +
+ + {children} +
+ ) +} +function ErrorBox({ message }) { return (
-
-

- Riscatta Codice Pro -

-

- Inserisci il tuo codice di attivazione (es. LP-XXXXXXXX) -

- - {message ? ( -
- {message} -
- ) : ( -
- {error && ( -
- {error} -
- )} - setCode(e.target.value.toUpperCase())} - placeholder="LP-XXXXXXXXXXXXXXXX" - required - style={{ ...inputStyle, marginBottom: '0.75rem', fontFamily: 'monospace', letterSpacing: '0.05em' }} - /> - -
- )} - - -
+

{message}

) } -// ── Google Icon SVG ──────────────────────────────────────────────────────────── +function SubmitButton({ loading, children }) { + return ( + + ) +} function GoogleIcon() { return ( - - - - - + + + + + ) } -// ── Shared inline styles ─────────────────────────────────────────────────────── - -const labelStyle = { - display: 'block', - fontSize: '0.78rem', - fontWeight: 600, - color: INK, - marginBottom: '0.4rem', - letterSpacing: '0.03em', - textTransform: 'uppercase', -} +// ── Shared style ─────────────────────────────────────────────────────────────── const inputStyle = { + display: 'flex', width: '100%', - padding: '0.65rem 0.875rem', - border: `1px solid ${BORDER}`, + height: 44, + border: '1px solid #E5E0D8', borderRadius: 0, - fontSize: '0.875rem', + backgroundColor: 'white', + padding: '0 1rem', + fontSize: '1rem', + color: '#1A1A1A', outline: 'none', boxSizing: 'border-box', - color: INK, - backgroundColor: '#FFFFFF', fontFamily: "'DM Sans', sans-serif", transition: 'border-color 0.15s', }