import { useState, useCallback, useEffect } from "react"; import { Upload, Image as ImageIcon, Loader2, Copy, Check } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { useToast } from "@/hooks/use-toast"; import { supabase } from "@/integrations/supabase/client"; import { User } from "@supabase/supabase-js"; interface ImageUploaderProps { user: User | null; } export const ImageUploader = ({ user }: ImageUploaderProps) => { const [isDragging, setIsDragging] = useState(false); const [imageFile, setImageFile] = useState(null); const [imagePreview, setImagePreview] = useState(null); const [isGenerating, setIsGenerating] = useState(false); const [altText, setAltText] = useState(null); const [isCopied, setIsCopied] = useState(false); const { toast } = useToast(); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragging(true); }, []); const handleDragLeave = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragging(false); }, []); const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragging(false); const file = e.dataTransfer.files[0]; if (file && file.type.startsWith('image/')) { handleImageFile(file); } else { toast({ title: "Invalid file type", description: "Please upload an image file", variant: "destructive", }); } }, [toast]); const handleFileInput = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { handleImageFile(file); } }; const handleImageFile = (file: File) => { setImageFile(file); setAltText(null); setIsCopied(false); const reader = new FileReader(); reader.onloadend = () => { setImagePreview(reader.result as string); }; reader.readAsDataURL(file); }; const generateAltText = async () => { if (!imageFile || !user) { toast({ title: "Authentication required", description: "Please sign in to generate alt text", variant: "destructive", }); return; } setIsGenerating(true); setAltText(null); const startTime = Date.now(); try { // Upload image to custom CDN const formData = new FormData(); formData.append('file', imageFile); const uploadResponse = await fetch('https://cdn.madebydanny.uk/upload', { method: 'POST', body: formData, }); if (!uploadResponse.ok) { throw new Error('Failed to upload image to CDN'); } const uploadData = await uploadResponse.json(); const imageUrl = uploadData.url; // Get auth token for authenticated request const { data: { session } } = await supabase.auth.getSession(); if (!session) { throw new Error('Not authenticated'); } // Call edge function to generate alt text (authenticated) const { data, error } = await supabase.functions.invoke('generate-alt-text', { body: { imageUrl, generationTimeStart: startTime } }); if (error) throw error; setAltText(data.altText); toast({ title: "Success!", description: "Alt text generated successfully", }); } catch (error: any) { console.error('Error generating alt text:', error); let errorMessage = "Failed to generate alt text"; if (error?.message?.includes('Rate limit')) { errorMessage = "Daily limit reached (20 per day). Try again tomorrow."; } else if (error?.message?.includes('Authentication')) { errorMessage = "Please sign in to continue"; } toast({ title: "Error", description: errorMessage, variant: "destructive", }); } finally { setIsGenerating(false); } }; const copyToClipboard = async () => { if (!altText) return; try { await navigator.clipboard.writeText(altText); setIsCopied(true); toast({ title: "Copied!", description: "Alt text copied to clipboard", }); setTimeout(() => setIsCopied(false), 2000); } catch (error) { toast({ title: "Error", description: "Failed to copy to clipboard", variant: "destructive", }); } }; return (

Upload Image

{!imagePreview ? (

Drag & drop your image here, or click to select

) : (
Preview
{altText && (

Generated Alt Text

{altText}

)}
)}
); };