| 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 { |
| |
| 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...'); |
|
|
| |
| const result = await dispatch(handleLinkedInCallback({ code, state })).unwrap(); |
|
|
| if (result.success) { |
| setStatus('success'); |
| setMessage('LinkedIn account linked successfully!'); |
| |
| |
| 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; |