feat: seo preview
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
import { Eye } from "lucide-react";
|
||||
import { BytescaleUploader } from "@/components/BytescaleUploader/BytescaleUploader";
|
||||
|
||||
import {
|
||||
@@ -60,6 +60,8 @@ const DAYS = [
|
||||
];
|
||||
|
||||
export default function DoctorPage() {
|
||||
const WEBSITE_URL = import.meta.env.VITE_WEBSITE_URL;
|
||||
|
||||
const [doctors, setDoctors] = useState<any[]>([]);
|
||||
const [departments, setDepartments] = useState<Department[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -100,6 +102,8 @@ export default function DoctorPage() {
|
||||
slug: "",
|
||||
tags: [],
|
||||
});
|
||||
const [openOgPreview, setOpenOgPreview] = useState(false);
|
||||
const [previewDoctor, setPreviewDoctor] = useState<any>(null);
|
||||
|
||||
const fetchAll = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -316,7 +320,10 @@ export default function DoctorPage() {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Current form state:", form); // Debug log to check form state
|
||||
function handlePreview(doc: any) {
|
||||
setPreviewDoctor(doc);
|
||||
setOpenOgPreview(true);
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
@@ -332,6 +339,24 @@ export default function DoctorPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const createSlug = (text: string) => {
|
||||
if (!text) return "";
|
||||
|
||||
return text
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/[^\w-]+/g, "")
|
||||
.replace(/--+/g, "-");
|
||||
};
|
||||
|
||||
const getDoctorUrl = (doctor: any) => {
|
||||
const slug = doctor?.seo?.slug || createSlug(doctor?.name);
|
||||
|
||||
return `${WEBSITE_URL}/${doctor?.doctorId}/${slug}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6 space-y-6">
|
||||
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
|
||||
@@ -492,6 +517,14 @@ export default function DoctorPage() {
|
||||
|
||||
<TableCell className="text-right">
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-9 w-9"
|
||||
onClick={() => handlePreview(doc)}
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
@@ -1126,6 +1159,97 @@ export default function DoctorPage() {
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Dialog open={openOgPreview} onOpenChange={setOpenOgPreview}>
|
||||
<DialogContent className="sm:!max-w-4xl overflow-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-xl">SEO Preview</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
{previewDoctor && (
|
||||
<div className="space-y-10 py-2">
|
||||
{/* SOCIAL MEDIA PREVIEW */}
|
||||
<div>
|
||||
<p className="mb-4 text-sm font-semibold text-muted-foreground">
|
||||
Social Media Preview (WhatsApp / Facebook)
|
||||
</p>
|
||||
|
||||
<a
|
||||
href={getDoctorUrl(previewDoctor)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block max-w-[560px] overflow-hidden rounded-xl border bg-white shadow-sm transition hover:shadow-md"
|
||||
>
|
||||
{/* IMAGE */}
|
||||
<div className="aspect-[1.91/1] overflow-hidden bg-muted">
|
||||
<img
|
||||
src={
|
||||
previewDoctor?.seo?.ogImage ||
|
||||
"https://placehold.co/1200x630?text=GG+Hospital"
|
||||
}
|
||||
alt="OG Preview"
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* CONTENT */}
|
||||
<div className="border-t bg-[#f0f2f5] px-4 py-3">
|
||||
<p className="truncate text-[11px] uppercase tracking-wide text-[#65676b]">
|
||||
gg-hospital.com
|
||||
</p>
|
||||
|
||||
<h3 className="mt-1 line-clamp-2 text-[18px] font-semibold leading-snug text-[#1c1e21]">
|
||||
{previewDoctor?.seo?.ogTitle ||
|
||||
previewDoctor?.seo?.seoTitle ||
|
||||
"GG Hospital"}
|
||||
</h3>
|
||||
|
||||
<p className="mt-1 line-clamp-2 text-[14px] text-[#65676b]">
|
||||
{previewDoctor?.seo?.ogDescription ||
|
||||
previewDoctor?.seo?.metaDescription ||
|
||||
"No description available"}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* GOOGLE SEARCH PREVIEW */}
|
||||
<div>
|
||||
<p className="mb-4 text-sm font-semibold text-muted-foreground">
|
||||
Google Search Preview
|
||||
</p>
|
||||
|
||||
<div className="rounded-xl border bg-white p-6">
|
||||
<a
|
||||
href={getDoctorUrl(previewDoctor)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block"
|
||||
>
|
||||
{/* URL */}
|
||||
<p className="truncate text-[14px] text-[#202124] hover:underline">
|
||||
{getDoctorUrl(previewDoctor)}
|
||||
</p>
|
||||
|
||||
{/* TITLE */}
|
||||
<h3 className="mt-1 text-[22px] leading-tight text-[#1a0dab] hover:underline">
|
||||
{previewDoctor?.seo?.seoTitle ||
|
||||
previewDoctor?.seo?.ogTitle ||
|
||||
"SEO title preview"}
|
||||
</h3>
|
||||
</a>
|
||||
|
||||
{/* DESCRIPTION */}
|
||||
<p className="mt-2 line-clamp-3 text-[14px] leading-6 text-[#4d5156]">
|
||||
{previewDoctor?.seo?.metaDescription ||
|
||||
previewDoctor?.seo?.ogDescription ||
|
||||
"No meta description available"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user