feat(01-04): create Google Sign-In button component
- Add cn() utility function for class name merging - Add Button component with default/outline/ghost variants - Add Card component with Header, Title, Description, Content, Footer - Add GoogleSignInButton with signInWithOAuth for Google provider - Italian text: 'Accedi con Google' - Redirects to /auth/callback after consent Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
68
src/components/auth/google-button.tsx
Normal file
68
src/components/auth/google-button.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
'use client'
|
||||
|
||||
import { createClient } from '@/lib/supabase/client'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useState } from 'react'
|
||||
|
||||
// Simple Google icon SVG
|
||||
function GoogleIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||
fill="#4285F4"
|
||||
/>
|
||||
<path
|
||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||
fill="#34A853"
|
||||
/>
|
||||
<path
|
||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||
fill="#FBBC05"
|
||||
/>
|
||||
<path
|
||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||
fill="#EA4335"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GoogleSignInButton() {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const supabase = createClient()
|
||||
|
||||
async function handleGoogleSignIn() {
|
||||
setLoading(true)
|
||||
|
||||
const { error } = await supabase.auth.signInWithOAuth({
|
||||
provider: 'google',
|
||||
options: {
|
||||
redirectTo: `${window.location.origin}/auth/callback`,
|
||||
queryParams: {
|
||||
access_type: 'offline',
|
||||
prompt: 'consent',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (error) {
|
||||
console.error('Google sign-in error:', error)
|
||||
setLoading(false)
|
||||
}
|
||||
// Note: No need to handle success - user is redirected to Google
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={handleGoogleSignIn}
|
||||
disabled={loading}
|
||||
className="w-full flex items-center justify-center gap-2"
|
||||
>
|
||||
<GoogleIcon className="w-5 h-5" />
|
||||
{loading ? 'Reindirizzamento...' : 'Accedi con Google'}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
37
src/components/ui/button.tsx
Normal file
37
src/components/ui/button.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { forwardRef, ButtonHTMLAttributes } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: 'default' | 'outline' | 'ghost'
|
||||
size?: 'default' | 'sm' | 'lg'
|
||||
}
|
||||
|
||||
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant = 'default', size = 'default', ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
|
||||
'disabled:pointer-events-none disabled:opacity-50',
|
||||
{
|
||||
'bg-blue-600 text-white hover:bg-blue-700': variant === 'default',
|
||||
'border border-gray-300 bg-white hover:bg-gray-50': variant === 'outline',
|
||||
'hover:bg-gray-100': variant === 'ghost',
|
||||
},
|
||||
{
|
||||
'h-10 px-4 py-2': size === 'default',
|
||||
'h-8 px-3 text-sm': size === 'sm',
|
||||
'h-12 px-6 text-lg': size === 'lg',
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Button.displayName = 'Button'
|
||||
|
||||
export { Button }
|
||||
69
src/components/ui/card.tsx
Normal file
69
src/components/ui/card.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { HTMLAttributes, forwardRef } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Card = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'rounded-lg border border-gray-200 bg-white shadow-sm',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
Card.displayName = 'Card'
|
||||
|
||||
const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
CardHeader.displayName = 'CardHeader'
|
||||
|
||||
const CardTitle = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn('text-2xl font-semibold leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
CardTitle.displayName = 'CardTitle'
|
||||
|
||||
const CardDescription = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLParagraphElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<p
|
||||
ref={ref}
|
||||
className={cn('text-sm text-gray-500', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
CardDescription.displayName = 'CardDescription'
|
||||
|
||||
const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
||||
)
|
||||
)
|
||||
CardContent.displayName = 'CardContent'
|
||||
|
||||
const CardFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('flex items-center p-6 pt-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
CardFooter.displayName = 'CardFooter'
|
||||
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
||||
6
src/lib/utils.ts
Normal file
6
src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
Reference in New Issue
Block a user