diff --git a/src/app/actions.ts b/src/app/actions.ts index cfbe202..6c5e2ae 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -2,6 +2,11 @@ import * as cheerio from "cheerio"; +interface FaqItem { + question: string; + answer: string; +} + export async function extractMetaData(url: string) { if (!url) { return { error: "URL is required." }; @@ -37,7 +42,45 @@ export async function extractMetaData(url: string) { "No description found"; const image = $('meta[property="og:image"]').attr("content") || null; - return { data: { title, description, image } }; + const faqData: FaqItem[] = []; + $('script[type="application/ld+json"]').each((i, el) => { + const jsonContent = $(el).html(); + if (!jsonContent) return; + + try { + const data = JSON.parse(jsonContent); + const graph = data["@graph"] || [data]; + + for (const item of graph) { + if (item["@type"] === "FAQPage" && Array.isArray(item.mainEntity)) { + item.mainEntity.forEach((qa: any) => { + if ( + qa["@type"] === "Question" && + qa.name && + qa.acceptedAnswer && + qa.acceptedAnswer.text + ) { + faqData.push({ + question: qa.name, + answer: qa.acceptedAnswer.text, + }); + } + }); + } + } + } catch (e) { + // Ignore parsing errors + } + }); + + return { + data: { + title, + description, + image, + faq: faqData.length > 0 ? faqData : null, + }, + }; } catch (error) { console.error(error); if (error instanceof Error && error.message.includes("Invalid URL")) { diff --git a/src/components/faq-display.tsx b/src/components/faq-display.tsx new file mode 100644 index 0000000..47a21fd --- /dev/null +++ b/src/components/faq-display.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; + +interface FaqDisplayProps { + faqs: { + question: string; + answer: string; + }[]; +} + +export function FaqDisplay({ faqs }: FaqDisplayProps) { + return ( +
+

+ FAQ Structured Data +

+ + {faqs.map((faq, index) => ( + + {faq.question} + {faq.answer} + + ))} + +
+ ); +} \ No newline at end of file diff --git a/src/components/meta-form.tsx b/src/components/meta-form.tsx index 35d9cda..f7df065 100644 --- a/src/components/meta-form.tsx +++ b/src/components/meta-form.tsx @@ -11,11 +11,13 @@ import { LengthIndicator } from "./length-indicator"; import { CopyButton } from "./copy-button"; import { SerpPreview } from "./serp-preview"; import { ResultsSkeleton } from "./results-skeleton"; +import { FaqDisplay } from "./faq-display"; interface MetaData { title: string; description: string; image?: string | null; + faq?: { question: string; answer: string }[] | null; } export function MetaForm() { @@ -159,6 +161,10 @@ export function MetaForm() { )} + + {metaData.faq && metaData.faq.length > 0 && ( + + )}
diff --git a/src/components/results-skeleton.tsx b/src/components/results-skeleton.tsx index 1a74823..ef04a8d 100644 --- a/src/components/results-skeleton.tsx +++ b/src/components/results-skeleton.tsx @@ -31,6 +31,14 @@ export function ResultsSkeleton() {
+
+ +
+ + + +
+