Backup: 2025-06-07 19:48 - Script test

[Restore from backup: vip-coordinator-backup-2025-06-07-19-48-script-test]
This commit is contained in:
2025-06-07 19:48:00 +02:00
parent 8fb00ec041
commit dc4655cef4
103 changed files with 16396 additions and 6143 deletions

View File

@@ -0,0 +1,109 @@
import React, { useEffect, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { apiCall } from '../utils/api';
const OAuthCallback: React.FC = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const [error, setError] = useState<string | null>(null);
const [processing, setProcessing] = useState(true);
useEffect(() => {
const handleCallback = async () => {
// Check for errors from OAuth provider
const errorParam = searchParams.get('error');
if (errorParam) {
setError(`Authentication failed: ${errorParam}`);
setProcessing(false);
return;
}
// Get the authorization code
const code = searchParams.get('code');
if (!code) {
setError('No authorization code received');
setProcessing(false);
return;
}
try {
// Exchange the code for a token
const response = await apiCall('/auth/google/exchange', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ code }),
});
if (response.error === 'pending_approval') {
// User needs approval
localStorage.setItem('authToken', response.data.token);
localStorage.setItem('user', JSON.stringify(response.data.user));
navigate('/pending-approval');
return;
}
if (response.data && response.data.token) {
// Success! Store the token and user data
localStorage.setItem('authToken', response.data.token);
localStorage.setItem('user', JSON.stringify(response.data.user));
// Redirect to dashboard
window.location.href = '/';
} else {
setError('Failed to authenticate');
}
} catch (err: any) {
console.error('OAuth callback error:', err);
if (err.message?.includes('pending_approval')) {
// This means the user was created but needs approval
navigate('/');
} else {
setError(err.message || 'Authentication failed');
}
} finally {
setProcessing(false);
}
};
handleCallback();
}, [navigate, searchParams]);
if (processing) {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex items-center justify-center">
<div className="bg-white rounded-2xl shadow-xl p-8 text-center">
<div className="animate-spin rounded-full h-12 w-12 border-4 border-blue-600 border-t-transparent mx-auto mb-4"></div>
<p className="text-lg font-medium text-slate-700">Completing sign in...</p>
</div>
</div>
);
}
if (error) {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex items-center justify-center">
<div className="bg-white rounded-2xl shadow-xl p-8 max-w-md text-center">
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h2 className="text-xl font-bold text-slate-800 mb-2">Authentication Failed</h2>
<p className="text-slate-600 mb-4">{error}</p>
<button
onClick={() => navigate('/')}
className="btn btn-primary"
>
Back to Login
</button>
</div>
</div>
);
}
return null;
};
export default OAuthCallback;