Lin / frontend /src /components /LinkedInAccount /LinkedInCallbackHandler.jsx
Zelyanoth's picture
fff
25f22bf
raw
history blame
7.21 kB
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation, useNavigate } from 'react-router-dom';
import { handleLinkedInCallback, clearLinkedInError } from '../../store/reducers/linkedinAccountsSlice';
const LinkedInCallbackHandler = () => {
const dispatch = useDispatch();
const location = useLocation();
const navigate = useNavigate();
const { oauthLoading, oauthError } = useSelector(state => state.linkedinAccounts);
const [status, setStatus] = useState('processing');
const [message, setMessage] = useState('Processing LinkedIn authentication...');
useEffect(() => {
const handleCallback = async () => {
try {
// Parse URL parameters
const urlParams = new URLSearchParams(location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
const error = urlParams.get('error');
if (error) {
setStatus('error');
setMessage(`Authentication failed: ${error}`);
return;
}
if (!code || !state) {
setStatus('error');
setMessage('Invalid callback parameters');
return;
}
setStatus('processing');
setMessage('Completing LinkedIn authentication...');
// Dispatch the callback action
const result = await dispatch(handleLinkedInCallback({ code, state })).unwrap();
if (result.success) {
setStatus('success');
setMessage('LinkedIn account linked successfully!');
// Redirect to sources page after a short delay
setTimeout(() => {
navigate('/sources');
}, 2000);
} else {
setStatus('error');
setMessage(result.message || 'Failed to link LinkedIn account');
}
} catch (error) {
console.error('Callback handler error:', error);
setStatus('error');
setMessage('An error occurred during authentication');
}
};
handleCallback();
}, [dispatch, location, navigate]);
const handleRetry = () => {
dispatch(clearLinkedInError());
navigate('/sources');
};
return (
<div className="linkedin-callback-handler">
<div className="callback-container">
<div className="callback-content">
{status === 'processing' && (
<div className="processing-state">
<div className="spinner"></div>
<h2>{message}</h2>
<p>Please wait while we complete the authentication process...</p>
</div>
)}
{status === 'success' && (
<div className="success-state">
<div className="success-icon"></div>
<h2>{message}</h2>
<p>Redirecting to your accounts...</p>
</div>
)}
{status === 'error' && (
<div className="error-state">
<div className="error-icon"></div>
<h2>{message}</h2>
<p>There was an issue with the LinkedIn authentication process.</p>
<div className="error-actions">
<button onClick={handleRetry} className="btn btn-primary">
Try Again
</button>
<button onClick={() => navigate('/sources')} className="btn btn-secondary">
Go to Sources
</button>
</div>
</div>
)}
</div>
</div>
<style jsx>{`
.linkedin-callback-handler {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 20px;
}
.callback-container {
background: white;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
padding: 40px;
max-width: 500px;
width: 100%;
text-align: center;
}
.callback-content {
min-height: 200px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.processing-state,
.success-state,
.error-state {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.spinner {
width: 48px;
height: 48px;
border: 4px solid #f3f3f3;
border-top: 4px solid #910029;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 24px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.success-icon {
width: 64px;
height: 64px;
background: #28a745;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 32px;
font-weight: bold;
margin-bottom: 24px;
}
.error-icon {
width: 64px;
height: 64px;
background: #dc3545;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 32px;
font-weight: bold;
margin-bottom: 24px;
}
.processing-state h2,
.success-state h2,
.error-state h2 {
margin: 0 0 16px 0;
font-size: 24px;
font-weight: 600;
color: #333;
}
.processing-state p,
.success-state p,
.error-state p {
margin: 0 0 24px 0;
color: #666;
font-size: 16px;
line-height: 1.5;
}
.error-actions {
display: flex;
gap: 12px;
justify-content: center;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
display: inline-block;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-primary {
background-color: #910029;
color: white;
}
.btn-primary:hover:not(:disabled) {
background-color: #7a0023;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover:not(:disabled) {
background-color: #5a6268;
}
@media (max-width: 600px) {
.callback-container {
padding: 24px;
margin: 10px;
}
.error-actions {
flex-direction: column;
width: 100%;
}
.btn {
width: 100%;
}
}
`}</style>
</div>
);
};
export default LinkedInCallbackHandler;