[dyad] Added live SERP preview - wrote 2 file(s)
This commit is contained in:
@@ -9,6 +9,7 @@ import { Globe, Edit, Check, Loader2, X, ImageOff } from "lucide-react";
|
||||
import { extractMetaData } from "@/app/actions";
|
||||
import { LengthIndicator } from "./length-indicator";
|
||||
import { CopyButton } from "./copy-button";
|
||||
import { SerpPreview } from "./serp-preview";
|
||||
|
||||
interface MetaData {
|
||||
title: string;
|
||||
@@ -21,7 +22,7 @@ export function MetaForm() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [metaData, setMetaData] = useState<MetaData | null>(null);
|
||||
|
||||
|
||||
const [isEditingTitle, setIsEditingTitle] = useState(false);
|
||||
const [isEditingDescription, setIsEditingDescription] = useState(false);
|
||||
|
||||
@@ -120,6 +121,17 @@ export function MetaForm() {
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div>
|
||||
<h3 className="font-semibold text-card-foreground mb-2">
|
||||
SERP Preview
|
||||
</h3>
|
||||
<SerpPreview
|
||||
title={editableTitle}
|
||||
description={editableDescription}
|
||||
url={url}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{metaData.image && (
|
||||
<div>
|
||||
<h3 className="font-semibold text-card-foreground mb-2">
|
||||
@@ -169,7 +181,9 @@ export function MetaForm() {
|
||||
) : (
|
||||
<Edit className="h-4 w-4" />
|
||||
)}
|
||||
<span className="sr-only">{isEditingTitle ? "Done editing" : "Edit"}</span>
|
||||
<span className="sr-only">
|
||||
{isEditingTitle ? "Done editing" : "Edit"}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -208,7 +222,9 @@ export function MetaForm() {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setIsEditingDescription(!isEditingDescription)}
|
||||
onClick={() =>
|
||||
setIsEditingDescription(!isEditingDescription)
|
||||
}
|
||||
className="h-8 w-8"
|
||||
>
|
||||
{isEditingDescription ? (
|
||||
@@ -216,12 +232,15 @@ export function MetaForm() {
|
||||
) : (
|
||||
<Edit className="h-4 w-4" />
|
||||
)}
|
||||
<span className="sr-only">{isEditingDescription ? "Done editing" : "Edit"}</span>
|
||||
<span className="sr-only">
|
||||
{isEditingDescription ? "Done editing" : "Edit"}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mt-1 mb-2">
|
||||
A brief summary of the page's content, ideally between 120 and 158 characters.
|
||||
A brief summary of the page's content, ideally between 120 and
|
||||
158 characters.
|
||||
</p>
|
||||
{isEditingDescription ? (
|
||||
<Textarea
|
||||
|
||||
52
src/components/serp-preview.tsx
Normal file
52
src/components/serp-preview.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
"use client";
|
||||
|
||||
import { Globe } from "lucide-react";
|
||||
|
||||
interface SerpPreviewProps {
|
||||
title: string;
|
||||
description: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export function SerpPreview({ title, description, url }: SerpPreviewProps) {
|
||||
const formatUrl = (fullUrl: string) => {
|
||||
try {
|
||||
let formattedUrl = fullUrl;
|
||||
if (!/^https?:\/\//i.test(fullUrl)) {
|
||||
formattedUrl = `https://${fullUrl}`;
|
||||
}
|
||||
const urlObject = new URL(formattedUrl);
|
||||
const path = urlObject.pathname === "/" ? "" : urlObject.pathname;
|
||||
// remove trailing slash
|
||||
const displayPath = path.endsWith('/') ? path.slice(0, -1) : path;
|
||||
return `${urlObject.hostname}${displayPath}`;
|
||||
} catch (error) {
|
||||
return fullUrl;
|
||||
}
|
||||
};
|
||||
|
||||
const displayUrl = formatUrl(url);
|
||||
|
||||
return (
|
||||
<div className="p-4 border rounded-lg bg-muted/50 w-full font-sans">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-7 h-7 bg-background rounded-full flex items-center justify-center border">
|
||||
<Globe className="w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-foreground font-medium -mb-0.5">
|
||||
{displayUrl.split("/")[0]}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">{displayUrl}</p>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-xl text-blue-600 dark:text-blue-400 mt-2 truncate font-medium">
|
||||
{title || "Meta Title Preview"}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{description ||
|
||||
"This is where the meta description will be displayed. It provides a brief summary of the page's content for search engine users."}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user