A simple SEO inspecter Tool, to get social media card previews
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 483 lines 20 kB view raw
1import React, { useState } from 'react'; 2import { Card, CardContent, CardHeader } from '@/components/ui/card'; 3import { Button } from '@/components/ui/button'; 4import { Separator } from '@/components/ui/separator'; 5import { Badge } from '@/components/ui/badge'; 6import { Code, Terminal, Book, ChevronRight, Copy, Check, Home, AlertTriangle, Zap } from 'lucide-react'; 7import { useToast } from '@/hooks/use-toast'; 8import { Link } from 'react-router-dom'; 9import { APIPlayground } from '@/components/APIPlayground'; 10 11const Docs = () => { 12 const [copiedSection, setCopiedSection] = useState<string | null>(null); 13 const { toast } = useToast(); 14 15 const copyToClipboard = (text: string, section: string) => { 16 navigator.clipboard.writeText(text); 17 setCopiedSection(section); 18 setTimeout(() => setCopiedSection(null), 2000); 19 toast({ 20 title: "Copied!", 21 description: "Code copied to clipboard", 22 }); 23 }; 24 25 const apiUrl = 'https://gjhfnqiknusundbktwxs.supabase.co/functions/v1/analyze-seo'; 26 27 return ( 28 <div className="min-h-screen bg-background"> 29 <div className="absolute inset-0 opacity-5"> 30 <div className="absolute inset-0" style={{ 31 backgroundImage: `radial-gradient(circle at 1px 1px, rgb(255,255,255) 1px, transparent 0)`, 32 backgroundSize: '20px 20px' 33 }}></div> 34 </div> 35 36 <div className="container mx-auto px-4 py-8 relative z-10"> 37 <div className="max-w-4xl mx-auto"> 38 {/* Header */} 39 <div className="mb-8 animate-fade-in"> 40 <Link to="/" className="inline-flex items-center gap-2 text-sm font-mono text-muted-foreground hover:text-foreground mb-4"> 41 <Home className="h-4 w-4" /> 42 Back to SEO Tester 43 </Link> 44 45 <div className="flex items-center gap-3 mb-4 p-4 bg-muted/50 rounded-lg border border-border/50"> 46 <Terminal className="h-5 w-5 text-primary" /> 47 <div className="flex items-center gap-1 text-xs font-mono"> 48 <span className="text-primary">$</span> 49 <span className="text-muted-foreground">api-documentation</span> 50 </div> 51 </div> 52 53 <h1 className="text-3xl font-bold text-foreground font-mono flex items-center gap-2"> 54 <Book className="h-8 w-8 text-primary" /> 55 SEO Analyzer API 56 </h1> 57 <p className="text-sm text-muted-foreground font-mono mt-2"> 58 RESTful API for programmatic SEO analysis 59 </p> 60 </div> 61 62 {/* Quick Start */} 63 <Card className="mb-6 border border-border bg-card"> 64 <CardHeader className="pb-3"> 65 <div className="flex items-center justify-between"> 66 <div className="flex items-center gap-2"> 67 <Code className="h-4 w-4 text-primary" /> 68 <span className="font-mono text-sm">Quick Start</span> 69 </div> 70 <Badge variant="outline" className="font-mono text-xs">GET STARTED</Badge> 71 </div> 72 </CardHeader> 73 <CardContent className="space-y-4"> 74 <div> 75 <div className="flex items-center justify-between mb-2"> 76 <span className="text-xs font-mono text-muted-foreground">ENDPOINT</span> 77 </div> 78 <div className="bg-muted/30 p-3 rounded font-mono text-sm border border-border/50 flex items-center justify-between"> 79 <code className="text-primary break-all">{apiUrl}</code> 80 <Button 81 size="sm" 82 variant="ghost" 83 onClick={() => copyToClipboard(apiUrl, 'endpoint')} 84 className="ml-2" 85 > 86 {copiedSection === 'endpoint' ? ( 87 <Check className="h-4 w-4 text-green-500" /> 88 ) : ( 89 <Copy className="h-4 w-4" /> 90 )} 91 </Button> 92 </div> 93 </div> 94 95 <div> 96 <div className="flex items-center gap-2 mb-2"> 97 <Badge variant="default" className="bg-green-600 text-xs">POST</Badge> 98 <span className="text-xs font-mono text-muted-foreground">METHOD</span> 99 </div> 100 </div> 101 </CardContent> 102 </Card> 103 104 {/* Request Format */} 105 <Card className="mb-6 border border-border bg-card"> 106 <CardHeader className="pb-3"> 107 <div className="flex items-center gap-2"> 108 <ChevronRight className="h-4 w-4 text-primary" /> 109 <span className="font-mono text-sm">Request Format</span> 110 </div> 111 </CardHeader> 112 <CardContent className="space-y-4"> 113 <div> 114 <div className="text-xs font-mono text-muted-foreground mb-2">HEADERS</div> 115 <div className="bg-muted/30 p-3 rounded font-mono text-sm border border-border/50"> 116 <div className="text-muted-foreground">Content-Type: <span className="text-foreground">application/json</span></div> 117 </div> 118 </div> 119 120 <div> 121 <div className="flex items-center justify-between mb-2"> 122 <span className="text-xs font-mono text-muted-foreground">REQUEST BODY</span> 123 <Button 124 size="sm" 125 variant="ghost" 126 onClick={() => copyToClipboard('{\n "url": "https://example.com"\n}', 'request')} 127 > 128 {copiedSection === 'request' ? ( 129 <Check className="h-4 w-4 text-green-500" /> 130 ) : ( 131 <Copy className="h-4 w-4" /> 132 )} 133 </Button> 134 </div> 135 <pre className="bg-muted/30 p-4 rounded font-mono text-sm border border-border/50 overflow-x-auto"> 136{`{ 137 "url": "https://example.com" 138}`} 139 </pre> 140 </div> 141 </CardContent> 142 </Card> 143 144 {/* Response Format */} 145 <Card className="mb-6 border border-border bg-card"> 146 <CardHeader className="pb-3"> 147 <div className="flex items-center gap-2"> 148 <ChevronRight className="h-4 w-4 text-primary" /> 149 <span className="font-mono text-sm">Response Format</span> 150 </div> 151 </CardHeader> 152 <CardContent> 153 <div className="flex items-center justify-between mb-2"> 154 <span className="text-xs font-mono text-muted-foreground">SUCCESS RESPONSE (200)</span> 155 <Button 156 size="sm" 157 variant="ghost" 158 onClick={() => copyToClipboard(`{ 159 "success": true, 160 "data": { 161 "title": "Page Title", 162 "description": "Meta description", 163 "ogTitle": "OG Title", 164 "ogDescription": "OG Description", 165 "ogImage": "https://example.com/image.jpg", 166 "ogType": "website", 167 "twitterCard": "summary_large_image", 168 "h1": "Main Heading", 169 "canonical": "https://example.com", 170 "lang": "en", 171 "viewport": "width=device-width, initial-scale=1", 172 "charset": "UTF-8", 173 "metaRobots": "index,follow" 174 }, 175 "score": { 176 "total": 85, 177 "breakdown": { 178 "basic": 35, 179 "social": 30, 180 "technical": 20 181 } 182 } 183}`, 'response')} 184 > 185 {copiedSection === 'response' ? ( 186 <Check className="h-4 w-4 text-green-500" /> 187 ) : ( 188 <Copy className="h-4 w-4" /> 189 )} 190 </Button> 191 </div> 192 <pre className="bg-muted/30 p-4 rounded font-mono text-xs border border-border/50 overflow-x-auto max-h-96"> 193{`{ 194 "success": true, 195 "data": { 196 "title": "Page Title", 197 "description": "Meta description", 198 "ogTitle": "OG Title", 199 "ogDescription": "OG Description", 200 "ogImage": "https://example.com/image.jpg", 201 "ogType": "website", 202 "twitterCard": "summary_large_image", 203 "twitterTitle": "Twitter Title", 204 "twitterDescription": "Twitter Description", 205 "twitterImage": "https://example.com/image.jpg", 206 "h1": "Main Heading", 207 "canonical": "https://example.com", 208 "keywords": "keyword1, keyword2", 209 "lang": "en", 210 "viewport": "width=device-width, initial-scale=1", 211 "charset": "UTF-8", 212 "metaRobots": "index,follow", 213 "url": "https://example.com" 214 }, 215 "score": { 216 "total": 85, 217 "breakdown": { 218 "basic": 35, 219 "social": 30, 220 "technical": 20 221 } 222 } 223}`} 224 </pre> 225 </CardContent> 226 </Card> 227 228 {/* API Playground */} 229 <Card className="mb-6 border border-border bg-card"> 230 <CardHeader className="pb-3"> 231 <div className="flex items-center gap-2"> 232 <Zap className="h-4 w-4 text-primary" /> 233 <span className="font-mono text-sm">Interactive Playground</span> 234 </div> 235 </CardHeader> 236 <CardContent> 237 <APIPlayground /> 238 </CardContent> 239 </Card> 240 241 {/* Batch Analysis */} 242 <Card className="mb-6 border border-border bg-card"> 243 <CardHeader className="pb-3"> 244 <div className="flex items-center gap-2"> 245 <ChevronRight className="h-4 w-4 text-primary" /> 246 <span className="font-mono text-sm">Batch Analysis</span> 247 </div> 248 </CardHeader> 249 <CardContent className="space-y-4"> 250 <p className="text-sm text-muted-foreground">Analyze multiple URLs in a single request (max 5 URLs):</p> 251 <div> 252 <div className="flex items-center justify-between mb-2"> 253 <span className="text-xs font-mono text-muted-foreground">BATCH REQUEST</span> 254 <Button 255 size="sm" 256 variant="ghost" 257 onClick={() => copyToClipboard('{\n "urls": [\n "https://example.com",\n "https://another-site.com"\n ]\n}', 'batch')} 258 > 259 {copiedSection === 'batch' ? ( 260 <Check className="h-4 w-4 text-green-500" /> 261 ) : ( 262 <Copy className="h-4 w-4" /> 263 )} 264 </Button> 265 </div> 266 <pre className="bg-muted/30 p-4 rounded font-mono text-sm border border-border/50 overflow-x-auto"> 267{`{ 268 "urls": [ 269 "https://example.com", 270 "https://another-site.com" 271 ] 272}`} 273 </pre> 274 </div> 275 </CardContent> 276 </Card> 277 278 {/* Response Schema */} 279 <Card className="mb-6 border border-border bg-card"> 280 <CardHeader className="pb-3"> 281 <div className="flex items-center gap-2"> 282 <Code className="h-4 w-4 text-primary" /> 283 <span className="font-mono text-sm">Response Schema</span> 284 </div> 285 </CardHeader> 286 <CardContent className="space-y-3"> 287 <div className="space-y-2 text-sm"> 288 <div className="flex gap-2"><code className="text-primary">success</code><span className="text-muted-foreground">boolean - Analysis success status</span></div> 289 <div className="flex gap-2"><code className="text-primary">cached</code><span className="text-muted-foreground">boolean - Whether result was cached</span></div> 290 <div className="flex gap-2"><code className="text-primary">data.title</code><span className="text-muted-foreground">string - Page title tag</span></div> 291 <div className="flex gap-2"><code className="text-primary">data.description</code><span className="text-muted-foreground">string - Meta description</span></div> 292 <div className="flex gap-2"><code className="text-primary">data.ogImage</code><span className="text-muted-foreground">string - Open Graph image URL</span></div> 293 <div className="flex gap-2"><code className="text-primary">score.total</code><span className="text-muted-foreground">number - Overall SEO score (0-100)</span></div> 294 <div className="flex gap-2"><code className="text-primary">score.breakdown</code><span className="text-muted-foreground">object - Score breakdown by category</span></div> 295 </div> 296 </CardContent> 297 </Card> 298 299 {/* Troubleshooting */} 300 <Card className="mb-6 border border-destructive/50 bg-destructive/5"> 301 <CardHeader className="pb-3"> 302 <div className="flex items-center gap-2"> 303 <AlertTriangle className="h-4 w-4 text-destructive" /> 304 <span className="font-mono text-sm">Troubleshooting</span> 305 </div> 306 </CardHeader> 307 <CardContent className="space-y-4"> 308 <div> 309 <h4 className="font-semibold text-sm mb-2">Rate Limiting (429)</h4> 310 <p className="text-sm text-muted-foreground">Maximum 10 requests per minute per IP. Wait 60 seconds before retrying.</p> 311 </div> 312 <Separator /> 313 <div> 314 <h4 className="font-semibold text-sm mb-2">Invalid URL (400)</h4> 315 <p className="text-sm text-muted-foreground">Ensure URL starts with http:// or https:// and is properly formatted.</p> 316 </div> 317 <Separator /> 318 <div> 319 <h4 className="font-semibold text-sm mb-2">Failed to Fetch (502)</h4> 320 <p className="text-sm text-muted-foreground">Target website may be blocking requests or experiencing issues. Try a different URL.</p> 321 </div> 322 <Separator /> 323 <div> 324 <h4 className="font-semibold text-sm mb-2">Caching</h4> 325 <p className="text-sm text-muted-foreground">Results are cached for 1 hour. Cached responses include "cached": true field.</p> 326 </div> 327 </CardContent> 328 </Card> 329 330 {/* Code Examples */} 331 <Card className="mb-6 border border-border bg-card"> 332 <CardHeader className="pb-3"> 333 <div className="flex items-center gap-2"> 334 <Code className="h-4 w-4 text-primary" /> 335 <span className="font-mono text-sm">Code Examples</span> 336 </div> 337 </CardHeader> 338 <CardContent className="space-y-6"> 339 {/* JavaScript/Fetch */} 340 <div> 341 <div className="flex items-center justify-between mb-2"> 342 <span className="text-xs font-mono text-muted-foreground">JAVASCRIPT (FETCH)</span> 343 <Button 344 size="sm" 345 variant="ghost" 346 onClick={() => copyToClipboard(`fetch('${apiUrl}', { 347 method: 'POST', 348 headers: { 349 'Content-Type': 'application/json', 350 }, 351 body: JSON.stringify({ 352 url: 'https://example.com' 353 }) 354}) 355 .then(response => response.json()) 356 .then(data => console.log(data)) 357 .catch(error => console.error('Error:', error));`, 'js-fetch')} 358 > 359 {copiedSection === 'js-fetch' ? ( 360 <Check className="h-4 w-4 text-green-500" /> 361 ) : ( 362 <Copy className="h-4 w-4" /> 363 )} 364 </Button> 365 </div> 366 <pre className="bg-muted/30 p-4 rounded font-mono text-xs border border-border/50 overflow-x-auto"> 367{`fetch('${apiUrl}', { 368 method: 'POST', 369 headers: { 370 'Content-Type': 'application/json', 371 }, 372 body: JSON.stringify({ 373 url: 'https://example.com' 374 }) 375}) 376 .then(response => response.json()) 377 .then(data => console.log(data)) 378 .catch(error => console.error('Error:', error));`} 379 </pre> 380 </div> 381 382 <Separator /> 383 384 {/* cURL */} 385 <div> 386 <div className="flex items-center justify-between mb-2"> 387 <span className="text-xs font-mono text-muted-foreground">CURL</span> 388 <Button 389 size="sm" 390 variant="ghost" 391 onClick={() => copyToClipboard(`curl -X POST '${apiUrl}' \\ 392 -H 'Content-Type: application/json' \\ 393 -d '{"url": "https://example.com"}'`, 'curl')} 394 > 395 {copiedSection === 'curl' ? ( 396 <Check className="h-4 w-4 text-green-500" /> 397 ) : ( 398 <Copy className="h-4 w-4" /> 399 )} 400 </Button> 401 </div> 402 <pre className="bg-muted/30 p-4 rounded font-mono text-xs border border-border/50 overflow-x-auto"> 403{`curl -X POST '${apiUrl}' \\ 404 -H 'Content-Type: application/json' \\ 405 -d '{"url": "https://example.com"}'`} 406 </pre> 407 </div> 408 409 <Separator /> 410 411 {/* Python */} 412 <div> 413 <div className="flex items-center justify-between mb-2"> 414 <span className="text-xs font-mono text-muted-foreground">PYTHON</span> 415 <Button 416 size="sm" 417 variant="ghost" 418 onClick={() => copyToClipboard(`import requests 419 420url = "${apiUrl}" 421payload = {"url": "https://example.com"} 422headers = {"Content-Type": "application/json"} 423 424response = requests.post(url, json=payload, headers=headers) 425print(response.json())`, 'python')} 426 > 427 {copiedSection === 'python' ? ( 428 <Check className="h-4 w-4 text-green-500" /> 429 ) : ( 430 <Copy className="h-4 w-4" /> 431 )} 432 </Button> 433 </div> 434 <pre className="bg-muted/30 p-4 rounded font-mono text-xs border border-border/50 overflow-x-auto"> 435{`import requests 436 437url = "${apiUrl}" 438payload = {"url": "https://example.com"} 439headers = {"Content-Type": "application/json"} 440 441response = requests.post(url, json=payload, headers=headers) 442print(response.json())`} 443 </pre> 444 </div> 445 </CardContent> 446 </Card> 447 448 {/* Error Responses */} 449 <Card className="border border-border bg-card"> 450 <CardHeader className="pb-3"> 451 <div className="flex items-center gap-2"> 452 <Terminal className="h-4 w-4 text-destructive" /> 453 <span className="font-mono text-sm">Error Responses</span> 454 </div> 455 </CardHeader> 456 <CardContent className="space-y-4"> 457 <div> 458 <Badge variant="destructive" className="text-xs mb-2">400 BAD REQUEST</Badge> 459 <pre className="bg-muted/30 p-3 rounded font-mono text-xs border border-border/50"> 460{`{ 461 "error": "URL parameter is required" 462}`} 463 </pre> 464 </div> 465 466 <div> 467 <Badge variant="destructive" className="text-xs mb-2">500 INTERNAL ERROR</Badge> 468 <pre className="bg-muted/30 p-3 rounded font-mono text-xs border border-border/50"> 469{`{ 470 "success": false, 471 "error": "Failed to fetch website" 472}`} 473 </pre> 474 </div> 475 </CardContent> 476 </Card> 477 </div> 478 </div> 479 </div> 480 ); 481}; 482 483export default Docs;