From 522caf6b1bb095e010318754daa6677afeba792a Mon Sep 17 00:00:00 2001 From: hamed <15238-genius.ha@users.noreply.drupalcode.org> Date: Sat, 4 Jul 2026 23:02:20 +0330 Subject: [PATCH] feat: implement PosterLight component and enhance SiteLogo functionality for improved poster rendering --- components/doctor/detailDoctor/List.js | 5 +- components/doctor/detailDoctor/Share.js | 39 ++++- components/doctor/poster/PosterLight.js | 210 ++++++++++++++++++++++++ components/doctor/poster/SiteLogo.js | 31 ++++ components/doctor/poster/index.js | 37 +---- 5 files changed, 290 insertions(+), 32 deletions(-) create mode 100644 components/doctor/poster/PosterLight.js create mode 100644 components/doctor/poster/SiteLogo.js diff --git a/components/doctor/detailDoctor/List.js b/components/doctor/detailDoctor/List.js index b304633..1621407 100644 --- a/components/doctor/detailDoctor/List.js +++ b/components/doctor/detailDoctor/List.js @@ -76,12 +76,13 @@ function List({ hiddenRef, data, qrReady }) { try { const fontEmbedCSS = await getVazirEmbedCss(); - imgData = await toPng(element, { + const options = { pixelRatio: 2, backgroundColor: "#ffffff", style: { opacity: "1" }, fontEmbedCSS, - }); + }; + imgData = await toPng(element, options); pxWidth = element.offsetWidth * 2; pxHeight = element.offsetHeight * 2; } catch { diff --git a/components/doctor/detailDoctor/Share.js b/components/doctor/detailDoctor/Share.js index ac5ed98..709ec17 100644 --- a/components/doctor/detailDoctor/Share.js +++ b/components/doctor/detailDoctor/Share.js @@ -7,16 +7,29 @@ import { Box, Button, Modal } from "@mui/material"; import CloseModalD from "@/components/icons/CloseModalD"; import List from "./List"; import Poster from "../poster"; +import PosterLight from "../poster/PosterLight"; + +const VARIANTS = [ + { key: "dark", label: "طرح تیره", swatch: "linear-gradient(135deg,#0B2A4A,#2E77C4)" }, + { key: "light", label: "طرح روشن", swatch: "linear-gradient(135deg,#FFFFFF,#CFE2F6)" }, +]; function Share({ data }) { const hiddenRef = useRef(); const [open, setOpen] = useState(false); const [qrReady, setQrReady] = useState(false); + const [variant, setVariant] = useState("dark"); const handleOpen = () => setOpen(true); const handleClose = () => setOpen(false); const handleQrReady = useCallback(() => setQrReady(true), []); + const changeVariant = (key) => { + if (key === variant) return; + setQrReady(false); + setVariant(key); + }; + return (
+ ))} +
diff --git a/components/doctor/poster/PosterLight.js b/components/doctor/poster/PosterLight.js new file mode 100644 index 0000000..fa672df --- /dev/null +++ b/components/doctor/poster/PosterLight.js @@ -0,0 +1,210 @@ +"use client"; +import { useEffect, useState } from "react"; +import DoctorAvatar from "@/app/component/DoctorAvatar"; +import QrImg from "./QrImg"; +import SiteLogo from "./SiteLogo"; +import { getStateInfoClient } from "@/lib/getStateInfoClient"; + +import StarDI from "@/components/icons/StarDI"; + +const ACCENT = "#16528C"; +const ACCENT_LIGHT = "#2E77C4"; +const ORANGE = "#F17732"; + +function PosterLight({ data, onQrReady }) { + const [city, setCity] = useState(null); + + useEffect(() => { + const { matchedCity } = getStateInfoClient(); + setCity(matchedCity || null); + }, []); + + const specialties = (data?.specialties ?? []).filter((s) => s?.name).slice(0, 4); + const services = (data?.expertise ?? []).filter((s) => s?.name); + const shownServices = services.slice(0, 8); + const moreServices = services.length - shownServices.length; + const rawAddress = data?.address?.[0]?.address; + const address = + rawAddress && rawAddress.length > 120 + ? `${rawAddress.slice(0, 120).trimEnd()}…` + : rawAddress; + const phones = (data?.address ?? []) + .map((a) => a?.phone) + .filter(Boolean) + .slice(0, 2); + + const badges = [ + Number(data?.experience) > 0 && { label: `${data.experience} سال تجربه` }, + (data?.point ?? 0) > 0 && { icon: , label: `امتیاز ${data.point}` }, + (data?.satisfaction ?? 0) > 0 && { label: `${data.satisfaction}% رضایت بیماران` }, + ].filter(Boolean); + + return ( +
+ {/* top brand band */} +
+
+ + + +
+ + {city?.site_name || "نوبت ۷۲۴"} + + + {city?.domain || "nobat724.com"} + +
+
+
+ +
+ {/* avatar overlapping band */} +
+
+ +
+
+ +

+ {data?.name} +

+ +
+ {specialties.map((s, i) => ( + + + {s.name} + + + ))} +
+ + {badges.length > 0 && ( +
+ {badges.map((b, i) => ( + + {i > 0 && ·} + {b.icon} + + {b.label} + + + ))} +
+ )} + + {data?.medical_system_code && ( +

+ کد نظام پزشکی:{" "} + + {data.medical_system_code} + +

+ )} + + {/* services */} + {shownServices.length > 0 && ( +
+

خدمات

+
+ {shownServices.map((s, i) => ( + + + + {s.name} + + + ))} + {moreServices > 0 && ( + + + {moreServices} خدمت دیگر + + )} +
+
+ )} + + {/* address / phone */} + {(address || phones.length > 0) && ( +
+ {address && ( +

+ آدرس: + {address} +

+ )} + {phones.length > 0 && ( +

+ تلفن: + {phones.map((p, i) => ( + + {p} + {i < phones.length - 1 ? " | " : ""} + + ))} +

+ )} +
+ )} +
+ + {/* footer */} +
+
+
+ +
+
+ + رزرو نوبت آنلاین — اسکن کنید + + + {city?.domain || "nobat724.com"} + +
+
+
+ + نوبت‌دهی ۲۴ ساعته + +
+
+
+ ); +} + +export default PosterLight; diff --git a/components/doctor/poster/SiteLogo.js b/components/doctor/poster/SiteLogo.js new file mode 100644 index 0000000..d847d82 --- /dev/null +++ b/components/doctor/poster/SiteLogo.js @@ -0,0 +1,31 @@ +function SiteLogo({ city, className, stroke = "#5559CE" }) { + if (city?.logo_url) { + return ( + logo + ); + } + return ( + + + + + + ); +} + +export default SiteLogo; diff --git a/components/doctor/poster/index.js b/components/doctor/poster/index.js index e2fd84c..67974c7 100644 --- a/components/doctor/poster/index.js +++ b/components/doctor/poster/index.js @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import DoctorAvatar from "@/app/component/DoctorAvatar"; import QrImg from "./QrImg"; +import SiteLogoBase from "./SiteLogo"; import { getStateInfoClient } from "@/lib/getStateInfoClient"; import StarDI from "@/components/icons/StarDI"; @@ -15,34 +16,8 @@ import Whatsapp from "@/components/icons/Whatsapp"; const POSTER_BG = "linear-gradient(160deg, #0B2A4A 0%, #16528C 52%, #2E77C4 100%)"; -function SiteLogo({ city, className }) { - if (city?.logo_url) { - return ( - logo - ); - } - return ( - - - - - - ); +function SiteLogo(props) { + return ; } function SectionTitle({ icon, children }) { @@ -68,7 +43,11 @@ function Poster({ data, onQrReady }) { const services = (data?.expertise ?? []).filter((s) => s?.name); const shownServices = services.slice(0, 8); const moreServices = services.length - shownServices.length; - const address = data?.address?.[0]?.address; + const rawAddress = data?.address?.[0]?.address; + const address = + rawAddress && rawAddress.length > 120 + ? `${rawAddress.slice(0, 120).trimEnd()}…` + : rawAddress; const phones = (data?.address ?? []) .map((a) => a?.phone) .filter(Boolean)