A simple SEO inspecter Tool, to get social media card previews
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;