A simple SEO inspecter Tool, to get social media card previews
at main 94 lines 3.1 kB view raw
1import { useState } from 'react'; 2import { Button } from '@/components/ui/button'; 3import { Input } from '@/components/ui/input'; 4import { Card } from '@/components/ui/card'; 5import { Loader2, Play, Copy, Check } from 'lucide-react'; 6import { toast } from 'sonner'; 7 8export const APIPlayground = () => { 9 const [testUrl, setTestUrl] = useState('https://example.com'); 10 const [loading, setLoading] = useState(false); 11 const [response, setResponse] = useState<any>(null); 12 const [copied, setCopied] = useState(false); 13 14 const handleTest = async () => { 15 setLoading(true); 16 try { 17 const res = await fetch(`${import.meta.env.VITE_SUPABASE_URL}/functions/v1/analyze-seo`, { 18 method: 'POST', 19 headers: { 20 'Content-Type': 'application/json', 21 'Authorization': `Bearer ${import.meta.env.VITE_SUPABASE_ANON_KEY}` 22 }, 23 body: JSON.stringify({ url: testUrl }) 24 }); 25 26 const data = await res.json(); 27 setResponse(data); 28 29 if (data.success) { 30 toast.success('Analysis completed!'); 31 } else { 32 toast.error(data.error || 'Analysis failed'); 33 } 34 } catch (error) { 35 toast.error('Failed to call API'); 36 setResponse({ error: 'Network error' }); 37 } finally { 38 setLoading(false); 39 } 40 }; 41 42 const copyResponse = () => { 43 navigator.clipboard.writeText(JSON.stringify(response, null, 2)); 44 setCopied(true); 45 toast.success('Copied to clipboard!'); 46 setTimeout(() => setCopied(false), 2000); 47 }; 48 49 return ( 50 <div className="space-y-4"> 51 <Card className="p-6 bg-card border-border"> 52 <h3 className="text-lg font-semibold mb-4 text-foreground">Try it out</h3> 53 <div className="flex gap-2"> 54 <Input 55 value={testUrl} 56 onChange={(e) => setTestUrl(e.target.value)} 57 placeholder="Enter URL to analyze" 58 className="flex-1" 59 /> 60 <Button 61 onClick={handleTest} 62 disabled={loading} 63 className="bg-primary text-primary-foreground hover:bg-primary/90" 64 > 65 {loading ? ( 66 <Loader2 className="h-4 w-4 animate-spin" /> 67 ) : ( 68 <Play className="h-4 w-4" /> 69 )} 70 </Button> 71 </div> 72 </Card> 73 74 {response && ( 75 <Card className="p-6 bg-muted border-border"> 76 <div className="flex justify-between items-center mb-4"> 77 <h4 className="font-semibold text-foreground">Response</h4> 78 <Button 79 variant="ghost" 80 size="sm" 81 onClick={copyResponse} 82 className="text-muted-foreground hover:text-foreground" 83 > 84 {copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />} 85 </Button> 86 </div> 87 <pre className="text-xs overflow-auto max-h-96 bg-background p-4 rounded border border-border text-foreground"> 88 {JSON.stringify(response, null, 2)} 89 </pre> 90 </Card> 91 )} 92 </div> 93 ); 94};