Files
smoothschedule/frontend/src/components/OAuthButtons.tsx
poduck 2e111364a2 Initial commit: SmoothSchedule multi-tenant scheduling platform
This commit includes:
- Django backend with multi-tenancy (django-tenants)
- React + TypeScript frontend with Vite
- Platform administration API with role-based access control
- Authentication system with token-based auth
- Quick login dev tools for testing different user roles
- CORS and CSRF configuration for local development
- Docker development environment setup

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 01:43:20 -05:00

157 lines
4.1 KiB
TypeScript

/**
* OAuth Buttons Component
* Displays OAuth provider buttons with icons and brand colors
*/
import React from 'react';
import { Loader2 } from 'lucide-react';
import { useInitiateOAuth, useOAuthProviders } from '../hooks/useOAuth';
interface OAuthButtonsProps {
onSuccess?: () => void;
disabled?: boolean;
}
// Provider configurations with colors and icons
const providerConfig: Record<
string,
{
name: string;
bgColor: string;
hoverColor: string;
textColor: string;
icon: string;
}
> = {
google: {
name: 'Google',
bgColor: 'bg-white',
hoverColor: 'hover:bg-gray-50',
textColor: 'text-gray-900',
icon: 'G',
},
apple: {
name: 'Apple',
bgColor: 'bg-black',
hoverColor: 'hover:bg-gray-900',
textColor: 'text-white',
icon: '',
},
facebook: {
name: 'Facebook',
bgColor: 'bg-[#1877F2]',
hoverColor: 'hover:bg-[#166FE5]',
textColor: 'text-white',
icon: 'f',
},
linkedin: {
name: 'LinkedIn',
bgColor: 'bg-[#0A66C2]',
hoverColor: 'hover:bg-[#095196]',
textColor: 'text-white',
icon: 'in',
},
microsoft: {
name: 'Microsoft',
bgColor: 'bg-[#00A4EF]',
hoverColor: 'hover:bg-[#0078D4]',
textColor: 'text-white',
icon: 'M',
},
x: {
name: 'X',
bgColor: 'bg-black',
hoverColor: 'hover:bg-gray-900',
textColor: 'text-white',
icon: 'X',
},
twitch: {
name: 'Twitch',
bgColor: 'bg-[#9146FF]',
hoverColor: 'hover:bg-[#7D3ACE]',
textColor: 'text-white',
icon: 'T',
},
};
const OAuthButtons: React.FC<OAuthButtonsProps> = ({ onSuccess, disabled = false }) => {
const { data: providers, isLoading } = useOAuthProviders();
const initiateMutation = useInitiateOAuth();
const handleOAuthClick = (providerId: string) => {
if (disabled || initiateMutation.isPending) return;
initiateMutation.mutate(providerId, {
onSuccess: () => {
onSuccess?.();
},
onError: (error) => {
console.error('OAuth initiation error:', error);
},
});
};
if (isLoading) {
return (
<div className="flex justify-center py-4">
<Loader2 className="h-5 w-5 animate-spin text-gray-400" />
</div>
);
}
if (!providers || providers.length === 0) {
return null;
}
return (
<div className="space-y-3">
{providers.map((provider) => {
const config = providerConfig[provider.name] || {
name: provider.display_name,
bgColor: 'bg-gray-600',
hoverColor: 'hover:bg-gray-700',
textColor: 'text-white',
icon: provider.display_name.charAt(0).toUpperCase(),
};
const isCurrentlyLoading =
initiateMutation.isPending && initiateMutation.variables === provider.name;
return (
<button
key={provider.name}
type="button"
onClick={() => handleOAuthClick(provider.name)}
disabled={disabled || initiateMutation.isPending}
className={`
w-full flex items-center justify-center gap-3 py-3 px-4
border rounded-lg shadow-sm text-sm font-medium
transition-all duration-200 ease-in-out transform active:scale-[0.98]
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-400
disabled:opacity-50 disabled:cursor-not-allowed
${config.bgColor} ${config.hoverColor} ${config.textColor}
${provider.name === 'google' ? 'border-gray-300 dark:border-gray-700' : 'border-transparent'}
`}
>
{isCurrentlyLoading ? (
<>
<Loader2 className="h-5 w-5 animate-spin" />
<span>Connecting...</span>
</>
) : (
<>
<span className="flex items-center justify-center w-5 h-5 font-bold text-sm">
{config.icon}
</span>
<span>Continue with {config.name}</span>
</>
)}
</button>
);
})}
</div>
);
};
export default OAuthButtons;