1// dashboard/src/pages/Login.jsx
2import { useState } from 'react';
3import { Link, useNavigate } from 'react-router-dom';
4import PublicLayout from '../components/Layout/PublicLayout';
6import { apiFetch } from '../lib/api';
9 const [email, setEmail] = useState('');
10 const [password, setPassword] = useState('');
11 const [loading, setLoading] = useState(false);
12 const navigate = useNavigate();
14 const handleLogin = async (e) => {
19 // ✅ Use direct fetch so future requests inherit Authorization logic
20 const res = await apiFetch('/users/login', {
22 headers: { 'Content-Type': 'application/json' },
23 body: JSON.stringify({ email, password, mfaCode: window._mfaCode || undefined }),
26 const contentType = res.headers.get('content-type') || '';
28 if (contentType.includes('application/json')) {
29 data = await res.json();
31 const text = await res.text();
32 try { data = JSON.parse(text); } catch { data = { error: text }; }
36 alert((data && (data.error || data.message)) || 'Login failed');
41 // ✅ Save JWT token for future API calls
43 localStorage.setItem('token', data.token);
45 alert('Login succeeded but no token returned.');
50 // Always redirect to dashboard after login
51 navigate('/dashboard');
53 console.error('Login error:', err);
54 alert('Cannot reach API. Please ensure the backend and database are running.');
62 <section className="login-hero">
63 <div className="login-hero-container">
64 <div className="login-hero-content">
65 <h1 className="login-hero-title">Welcome Back</h1>
66 <p className="login-hero-subtitle">Sign in to access your dashboard and manage your MSP clients.</p>
67 <form onSubmit={handleLogin} className="login-form">
68 <div className="form-group">
69 <label htmlFor="email">Email</label>
74 placeholder="Enter your email"
76 onChange={(e) => setEmail(e.target.value)}
81 <div className="form-group">
82 <label htmlFor="password">Password</label>
87 placeholder="Enter your password"
89 onChange={(e) => setPassword(e.target.value)}
94 <div className="form-group">
95 <label htmlFor="mfa">MFA Code (if enabled)</label>
104 onChange={(e) => (window._mfaCode = e.target.value)}
107 <button type="submit" className="login-button" disabled={loading}>
108 {loading ? 'Signing In...' : 'Sign In'}
111 <div className="login-footer">
112 <p><Link to="/forgot-password">Forgot your password?</Link></p>
114 Don't have an account? <Link to="/">Contact Sales</Link>
118 <div className="login-hero-image">
119 <div className="dashboard-preview">
121 src="/dashboard-preview.png"
122 alt="RMM+PSA Dashboard Preview - Ticket Management Interface"
123 className="preview-screenshot"