294 lines
8.6 KiB
TypeScript
294 lines
8.6 KiB
TypeScript
import React from 'react';
|
|
|
|
type LoginStatus = 'idle' | 'waiting' | 'success' | 'error';
|
|
|
|
interface LoginPromptProps {
|
|
authState: string;
|
|
errorMessage?: string;
|
|
onLogin: () => void;
|
|
onRetry: () => void;
|
|
loginStatus?: LoginStatus;
|
|
}
|
|
|
|
export function LoginPrompt({
|
|
authState,
|
|
errorMessage,
|
|
onLogin,
|
|
onRetry,
|
|
loginStatus = 'idle'
|
|
}: LoginPromptProps) {
|
|
// Show success state when login completed
|
|
if (loginStatus === 'success') {
|
|
return (
|
|
<div style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
minHeight: '100vh',
|
|
backgroundColor: '#1a1a1a',
|
|
padding: '2rem',
|
|
}}>
|
|
<div style={{
|
|
maxWidth: '500px',
|
|
width: '100%',
|
|
padding: '2rem',
|
|
backgroundColor: '#252525',
|
|
borderRadius: '12px',
|
|
boxShadow: '0 4px 24px rgba(0, 0, 0, 0.3)',
|
|
textAlign: 'center',
|
|
}}>
|
|
<div style={{
|
|
width: '80px',
|
|
height: '80px',
|
|
margin: '0 auto 1.5rem',
|
|
backgroundColor: '#1a472a',
|
|
borderRadius: '50%',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
fontSize: '2.5rem',
|
|
color: '#4ade80',
|
|
animation: 'scaleIn 0.3s ease-out',
|
|
}}>
|
|
✓
|
|
</div>
|
|
<style>{`
|
|
@keyframes scaleIn {
|
|
from { transform: scale(0); opacity: 0; }
|
|
to { transform: scale(1); opacity: 1; }
|
|
}
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(10px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
`}</style>
|
|
|
|
<h1 style={{
|
|
color: '#4ade80',
|
|
fontSize: '1.75rem',
|
|
marginBottom: '0.5rem',
|
|
fontWeight: 600,
|
|
animation: 'fadeIn 0.4s ease-out 0.1s both',
|
|
}}>
|
|
Login Successful!
|
|
</h1>
|
|
|
|
<p style={{
|
|
color: '#aaa',
|
|
fontSize: '1rem',
|
|
marginBottom: '1rem',
|
|
lineHeight: 1.5,
|
|
animation: 'fadeIn 0.4s ease-out 0.2s both',
|
|
}}>
|
|
You're now connected to iRacing.
|
|
</p>
|
|
|
|
<p style={{
|
|
color: '#666',
|
|
fontSize: '0.9rem',
|
|
animation: 'fadeIn 0.4s ease-out 0.3s both',
|
|
}}>
|
|
Redirecting...
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const getStateMessage = () => {
|
|
switch (authState) {
|
|
case 'EXPIRED':
|
|
return 'Your iRacing session has expired. Please log in again to continue.';
|
|
case 'LOGGED_OUT':
|
|
return 'You have been logged out. Please log in to use GridPilot.';
|
|
case 'UNKNOWN':
|
|
return errorMessage
|
|
? `Unable to verify authentication: ${errorMessage}`
|
|
: 'Unable to verify your authentication status.';
|
|
default:
|
|
return null; // Will show explanation instead
|
|
}
|
|
};
|
|
|
|
const stateMessage = getStateMessage();
|
|
const isWaiting = loginStatus === 'waiting';
|
|
|
|
return (
|
|
<div style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
minHeight: '100vh',
|
|
backgroundColor: '#1a1a1a',
|
|
padding: '2rem',
|
|
}}>
|
|
<div style={{
|
|
maxWidth: '500px',
|
|
width: '100%',
|
|
padding: '2rem',
|
|
backgroundColor: '#252525',
|
|
borderRadius: '12px',
|
|
boxShadow: '0 4px 24px rgba(0, 0, 0, 0.3)',
|
|
textAlign: 'center',
|
|
}}>
|
|
<div style={{
|
|
width: '80px',
|
|
height: '80px',
|
|
margin: '0 auto 1.5rem',
|
|
backgroundColor: '#333',
|
|
borderRadius: '50%',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
fontSize: '2.5rem',
|
|
}}>
|
|
{isWaiting ? '⏳' : '🔐'}
|
|
</div>
|
|
|
|
<h1 style={{
|
|
color: '#fff',
|
|
fontSize: '1.75rem',
|
|
marginBottom: '0.5rem',
|
|
fontWeight: 600,
|
|
}}>
|
|
{isWaiting ? 'Waiting for Login...' : 'iRacing Login Required'}
|
|
</h1>
|
|
|
|
{stateMessage ? (
|
|
<p style={{
|
|
color: '#aaa',
|
|
fontSize: '1rem',
|
|
marginBottom: '2rem',
|
|
lineHeight: 1.5,
|
|
}}>
|
|
{stateMessage}
|
|
</p>
|
|
) : (
|
|
<div style={{
|
|
color: '#aaa',
|
|
fontSize: '1rem',
|
|
marginBottom: '2rem',
|
|
lineHeight: 1.6,
|
|
textAlign: 'left',
|
|
}}>
|
|
<p style={{ marginBottom: '1rem' }}>
|
|
<strong style={{ color: '#fff' }}>Why do I need to log in?</strong>
|
|
</p>
|
|
<p style={{ marginBottom: '0.75rem' }}>
|
|
GridPilot needs to access your iRacing account to create and manage hosted sessions on your behalf. This requires authentication with iRacing's website.
|
|
</p>
|
|
<ul style={{
|
|
margin: '0.75rem 0',
|
|
paddingLeft: '1.25rem',
|
|
color: '#888',
|
|
}}>
|
|
<li style={{ marginBottom: '0.5rem' }}>✓ Your credentials are entered directly on iRacing.com</li>
|
|
<li style={{ marginBottom: '0.5rem' }}>✓ GridPilot never sees or stores your password</li>
|
|
<li>✓ Session cookies are stored locally for convenience</li>
|
|
</ul>
|
|
</div>
|
|
)}
|
|
|
|
{isWaiting ? (
|
|
<div style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
gap: '1rem',
|
|
}}>
|
|
<div style={{
|
|
width: '40px',
|
|
height: '40px',
|
|
border: '3px solid #333',
|
|
borderTopColor: '#007bff',
|
|
borderRadius: '50%',
|
|
animation: 'spin 1s linear infinite',
|
|
}} />
|
|
<style>{`
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
`}</style>
|
|
<p style={{
|
|
color: '#aaa',
|
|
fontSize: '0.95rem',
|
|
}}>
|
|
A browser window has opened. Please log in to iRacing.
|
|
</p>
|
|
<p style={{
|
|
color: '#666',
|
|
fontSize: '0.85rem',
|
|
}}>
|
|
This window will update automatically when login is detected.
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '0.75rem',
|
|
}}>
|
|
<button
|
|
onClick={onLogin}
|
|
style={{
|
|
padding: '1rem 2rem',
|
|
backgroundColor: '#007bff',
|
|
color: '#fff',
|
|
border: 'none',
|
|
borderRadius: '8px',
|
|
cursor: 'pointer',
|
|
fontSize: '1.1rem',
|
|
fontWeight: 600,
|
|
transition: 'background-color 0.2s',
|
|
}}
|
|
onMouseOver={(e) => e.currentTarget.style.backgroundColor = '#0056b3'}
|
|
onMouseOut={(e) => e.currentTarget.style.backgroundColor = '#007bff'}
|
|
>
|
|
Log in to iRacing
|
|
</button>
|
|
|
|
{(authState === 'UNKNOWN' || errorMessage) && (
|
|
<button
|
|
onClick={onRetry}
|
|
style={{
|
|
padding: '0.75rem 1.5rem',
|
|
backgroundColor: 'transparent',
|
|
color: '#aaa',
|
|
border: '1px solid #555',
|
|
borderRadius: '8px',
|
|
cursor: 'pointer',
|
|
fontSize: '0.95rem',
|
|
transition: 'all 0.2s',
|
|
}}
|
|
onMouseOver={(e) => {
|
|
e.currentTarget.style.borderColor = '#777';
|
|
e.currentTarget.style.color = '#fff';
|
|
}}
|
|
onMouseOut={(e) => {
|
|
e.currentTarget.style.borderColor = '#555';
|
|
e.currentTarget.style.color = '#aaa';
|
|
}}
|
|
>
|
|
Retry Connection
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{!isWaiting && (
|
|
<p style={{
|
|
color: '#666',
|
|
fontSize: '0.85rem',
|
|
marginTop: '2rem',
|
|
lineHeight: 1.4,
|
|
}}>
|
|
A browser window will open for you to log in securely to iRacing.
|
|
The window will close automatically once login is complete.
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
} |