From fc491f4050ac1794fa6e3c84d0a5f644e2bcd0eb Mon Sep 17 00:00:00 2001 From: rishalkv Date: Mon, 25 May 2026 16:20:47 +0530 Subject: [PATCH 1/3] feat: seo preview --- frontend/src/pages/Doctor.tsx | 128 +++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/Doctor.tsx b/frontend/src/pages/Doctor.tsx index b84ee08..2b7ce0d 100644 --- a/frontend/src/pages/Doctor.tsx +++ b/frontend/src/pages/Doctor.tsx @@ -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([]); const [departments, setDepartments] = useState([]); const [loading, setLoading] = useState(true); @@ -100,6 +102,8 @@ export default function DoctorPage() { slug: "", tags: [], }); + const [openOgPreview, setOpenOgPreview] = useState(false); + const [previewDoctor, setPreviewDoctor] = useState(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 (
@@ -492,6 +517,14 @@ export default function DoctorPage() {
+
); } From fa06126219b9be6319f2207bd27f2aa353b0859e Mon Sep 17 00:00:00 2001 From: rishalkv Date: Tue, 26 May 2026 11:38:34 +0530 Subject: [PATCH 2/3] chore: add seo reusable component --- .../src/components/SeoPreview/SeoPreview.tsx | 131 ++++++++++++++++++ frontend/src/pages/Doctor.tsx | 98 +------------ 2 files changed, 138 insertions(+), 91 deletions(-) create mode 100644 frontend/src/components/SeoPreview/SeoPreview.tsx diff --git a/frontend/src/components/SeoPreview/SeoPreview.tsx b/frontend/src/components/SeoPreview/SeoPreview.tsx new file mode 100644 index 0000000..84139ca --- /dev/null +++ b/frontend/src/components/SeoPreview/SeoPreview.tsx @@ -0,0 +1,131 @@ +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; + +interface SeoPreviewData { + seo?: { + ogImage?: string; + ogTitle?: string; + seoTitle?: string; + ogDescription?: string; + metaDescription?: string; + slug?: string; + }; + doctorId?: string; + name?: string; +} + +interface SeoPreviewProps { + open: boolean; + onOpenChange: (open: boolean) => void; + previewData?: SeoPreviewData | null; + url?: string; + title?: string; +} + +export default function SeoPreview({ + open, + onOpenChange, + previewData, + url, + title = "SEO Preview", +}: SeoPreviewProps) { + const previewUrl = url || "#"; + const imageUrl = + previewData?.seo?.ogImage || "https://placehold.co/1200x630?text=GG+Hospital"; + const ogTitle = + previewData?.seo?.ogTitle || previewData?.seo?.seoTitle || "GG Hospital"; + const ogDescription = + previewData?.seo?.ogDescription || previewData?.seo?.metaDescription || + "No description available"; + const searchTitle = + previewData?.seo?.seoTitle || previewData?.seo?.ogTitle || "SEO title preview"; + const searchDescription = + previewData?.seo?.metaDescription || previewData?.seo?.ogDescription || + "No meta description available"; + + return ( + + + + {title} + + + {previewData ? ( +
+
+

+ Social Media Preview (WhatsApp / Facebook) +

+ + +
+ OG Preview +
+ +
+

+ gg-hospital.com +

+ +

+ {ogTitle} +

+ +

+ {ogDescription} +

+
+
+
+ +
+

+ Google Search Preview +

+ +
+ +

+ {previewUrl} +

+ +

+ {searchTitle} +

+
+ +

+ {searchDescription} +

+
+
+
+ ) : ( +
+ No preview data available. +
+ )} + + + + +
+
+ ); +} diff --git a/frontend/src/pages/Doctor.tsx b/frontend/src/pages/Doctor.tsx index 2b7ce0d..c127f6f 100644 --- a/frontend/src/pages/Doctor.tsx +++ b/frontend/src/pages/Doctor.tsx @@ -28,6 +28,7 @@ import { DialogTitle, DialogFooter, } from "@/components/ui/dialog"; +import SeoPreview from "@/components/SeoPreview/SeoPreview"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Switch } from "@/components/ui/switch"; @@ -1159,97 +1160,12 @@ export default function DoctorPage() { - - - - SEO Preview - - - {previewDoctor && ( -
- {/* SOCIAL MEDIA PREVIEW */} - - - {/* GOOGLE SEARCH PREVIEW */} -
-

- Google Search Preview -

- -
- - {/* URL */} -

- {getDoctorUrl(previewDoctor)} -

- - {/* TITLE */} -

- {previewDoctor?.seo?.seoTitle || - previewDoctor?.seo?.ogTitle || - "SEO title preview"} -

-
- - {/* DESCRIPTION */} -

- {previewDoctor?.seo?.metaDescription || - previewDoctor?.seo?.ogDescription || - "No meta description available"} -

-
-
-
- )} -
-
+
); } From c2b54725fedb4778ed2ac7fc30b5fe2de5bcc82f Mon Sep 17 00:00:00 2001 From: rishalkv Date: Tue, 26 May 2026 11:52:29 +0530 Subject: [PATCH 3/3] fix: og image update --- backend/src/controllers/doctor.controller.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/src/controllers/doctor.controller.js b/backend/src/controllers/doctor.controller.js index ae4cdca..38b42a3 100644 --- a/backend/src/controllers/doctor.controller.js +++ b/backend/src/controllers/doctor.controller.js @@ -357,8 +357,10 @@ export const updateDoctor = async (req, res) => { focusKeyphrase, slug, tags, + ogImage, specializations, } = req.body; + if (!doctorId) { return res.status(400).json({ success: false, @@ -427,6 +429,7 @@ export const updateDoctor = async (req, res) => { data: { seoTitle, metaDescription, + ogImage, focusKeyphrase, slug: slug ? slug : null, tags: tags || [], @@ -435,6 +438,7 @@ export const updateDoctor = async (req, res) => { } else { const seo = await prisma.seo.create({ data: { + ogImage, seoTitle, metaDescription, focusKeyphrase,