Files
nobat724_front/components/doctor/poster/index.js
T

275 lines
11 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect, useState } from "react";
import DoctorAvatar from "@/app/component/DoctorAvatar";
import QrImg from "./QrImg";
import SiteLogoBase from "@/components/common/SiteLogo";
import { getStateInfoClient } from "@/lib/getStateInfoClient";
import { posterSpecialtyLine, splitSpecialties } from "@/lib/specialtyDisplay";
import { doctorTitle } from "@/helper";
import StarDI from "@/components/icons/StarDI";
import ClipBoard from "@/components/icons/ClipBoard";
import AddressPoster from "@/components/icons/AddressPoster";
import TelefonPoster from "@/components/icons/TelefonPoster";
import Instagram from "@/components/icons/Instagram";
import Telegram from "@/components/icons/Telegram";
import Whatsapp from "@/components/icons/Whatsapp";
import { FEATURES } from "@/config/features";
const POSTER_BG =
"linear-gradient(160deg, #0B2A4A 0%, #16528C 52%, #2E77C4 100%)";
function SiteLogo(props) {
return <SiteLogoBase stroke="#8FB6FF" {...props} />;
}
function SectionTitle({ icon, children }) {
return (
<div className="flex items-center gap-[12px] mb-[20px]">
<span className="flex items-center justify-center w-[40px] h-[40px] rounded-[12px] bg-white/12">
{icon}
</span>
<span className="text-white text-[26px] font-bold">{children}</span>
</div>
);
}
function Poster({ data, onQrReady }) {
const [city, setCity] = useState(null);
useEffect(() => {
const { matchedCity } = getStateInfoClient();
setCity(matchedCity || null);
}, []);
// برش بر اساس طول متن است نه عدد ثابت: چهار چیپ با نام‌های بلند به سه ردیف
// می‌رفت، بخش hero را بلند می‌کرد و بخش‌های پایین را از کادر ثابت بیرون می‌انداخت.
const { primary, rest } = splitSpecialties(data?.specialties);
const subSpecialties = posterSpecialtyLine(rest, 78);
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 social = city?.social_media ?? {};
const socialItems = [
social.instagram && <Instagram key="ig" />,
social.telegram && <Telegram key="tg" />,
social.whatsapp && <Whatsapp key="wa" />,
].filter(Boolean);
const badges = [
Number(data?.experience) > 0 && {
icon: <span className="text-[#F6B93B] text-[20px] leading-none"></span>,
label: `${data.experience} سال تجربه`,
},
FEATURES.posterRatings && (data?.point ?? 0) > 0 && {
icon: <StarDI />,
label: `امتیاز ${data.point}`,
},
FEATURES.posterRatings && (data?.satisfaction ?? 0) > 0 && {
icon: <span className="text-[#4ADE80] text-[20px] leading-none"></span>,
label: `${data.satisfaction}% رضایت`,
},
].filter(Boolean);
return (
<div
dir="rtl"
className="relative w-[1080px] h-[1527px] overflow-hidden flex flex-col p-[64px] text-right"
style={{ background: POSTER_BG }}
>
<div className="pointer-events-none absolute -top-[180px] -left-[180px] w-[520px] h-[520px] rounded-full bg-[#57A6FF]/20 blur-[80px]" />
<div className="pointer-events-none absolute bottom-[120px] -right-[160px] w-[460px] h-[460px] rounded-full bg-[#0B2A4A]/40 blur-[80px]" />
{/* header */}
<header className="relative flex items-center justify-between">
<div className="flex items-center gap-[16px]">
<SiteLogo city={city} className="h-[64px] w-[64px]" />
<div className="flex flex-col">
<span className="text-white text-[30px] font-extrabold leading-tight">
{city?.site_name || "نوبت ۷۲۴"}
</span>
<span className="text-[#9FC1E6] text-[18px] font-medium" dir="ltr">
{city?.domain || "nobat724.com"}
</span>
</div>
</div>
<div className="flex items-center rounded-full bg-[#F59E0B] px-[28px] py-[14px]">
<span className="text-[#1A1200] text-[22px] font-bold leading-none">
نوبتدهی آنلاین
</span>
</div>
</header>
{city?.slogan && (
<p className="relative mt-[20px] text-[#C7DBF2] text-[22px] font-medium">
{city.slogan}
</p>
)}
{/* hero */}
<section className="relative mt-[40px] rounded-[32px] bg-white/8 border border-white/12 p-[40px] flex items-center gap-[36px]">
<div className="shrink-0 rounded-full p-[8px] bg-gradient-to-br from-[#5AA6FF] to-[#F59E0B]">
<div className="rounded-full p-[6px] bg-[#0B2A4A]">
<DoctorAvatar
doctor={data}
size={220}
priority
className="rounded-full !object-cover w-[220px] h-[220px]"
/>
</div>
</div>
<div className="flex-1 min-w-0">
<h1 className="text-white text-[46px] font-extrabold leading-tight">
{doctorTitle(data?.name)}
</h1>
{primary && (
<div className="mt-[16px] flex flex-wrap items-center gap-[12px]">
<span className="flex items-center rounded-full bg-[#F59E0B]/18 border border-[#F59E0B]/40 px-[18px] py-[9px]">
<span className="text-[#FCD9A0] text-[22px] font-semibold leading-none">
{primary.name}
</span>
</span>
</div>
)}
{/* زیرتخصص‌ها یک خط متنی‌اند نه چیپ: در چاپ نه کلیک هست نه Popover، پس
«+N» بی‌معناست و متن خوانا جایش را می‌گیرد. max-h تور ایمنیِ کادر است
اگر بودجهٔ کاراکتر روزی کوتاه تنظیم شود. */}
{subSpecialties.text && (
<p className="mt-[12px] max-h-[80px] overflow-hidden text-[#C7DBF2] text-[20px] font-medium leading-[40px]">
{subSpecialties.text}
{subSpecialties.hidden > 0 && ` و ${subSpecialties.hidden} تخصص دیگر`}
</p>
)}
<div className="flex flex-wrap gap-[12px] mt-[18px]">
{badges.map((b, i) => (
<span
key={i}
className="flex items-center gap-[8px] rounded-full bg-white/10 border border-white/15 px-[18px] py-[11px]"
>
{b.icon}
<span className="text-[#EAF2FB] text-[22px] font-semibold leading-none whitespace-nowrap">
{b.label}
</span>
</span>
))}
</div>
{data?.medical_system_code && (
<p className="mt-[16px] text-[#9FC1E6] text-[20px]">
کد نظام پزشکی:{" "}
<span dir="ltr" className="font-semibold text-[#E7EEF6]">
{data.medical_system_code}
</span>
</p>
)}
</div>
</section>
{/* body */}
{/* کارت‌ها به هم چسبیده می‌مانند و فاصلهٔ اضافیِ کاغذ A4 بالای فوتر می‌نشیند:
حفرهٔ وسطِ صفحه شبیه محتوای جاافتاده دیده می‌شد. */}
<section className="relative mt-[36px] flex-1 flex flex-col gap-[28px]">
{shownServices.length > 0 && (
<div className="rounded-[28px] bg-white/6 border border-white/10 p-[32px]">
<SectionTitle icon={<ClipBoard />}>خدمات</SectionTitle>
<div className="flex flex-wrap items-center gap-x-[12px] gap-y-[14px]">
{shownServices.map((s, i) => (
<span
key={i}
className="flex items-center gap-[10px] rounded-full bg-white/8 border border-white/10 px-[18px] py-[10px]"
>
<span className="w-[10px] h-[10px] rounded-full bg-[#F59E0B] shrink-0" />
<span className="text-[#E7EEF6] text-[22px] font-normal leading-none whitespace-nowrap">
{s.name}
</span>
</span>
))}
{moreServices > 0 && (
<span className="text-[#9FC1E6] text-[22px]">
+ {moreServices} خدمت دیگر
</span>
)}
</div>
</div>
)}
{(address || phones.length > 0) && (
<div className="rounded-[28px] bg-white/6 border border-white/10 p-[32px]">
{address && (
<div className="mb-[24px]">
<SectionTitle icon={<AddressPoster />}>آدرس</SectionTitle>
<p className="text-[#E7EEF6] text-[23px] font-normal leading-[1.7] line-clamp-2">
{address}
</p>
</div>
)}
{phones.length > 0 && (
<div>
<SectionTitle icon={<TelefonPoster />}>تلفن</SectionTitle>
<div className="flex flex-wrap gap-[16px]">
{phones.map((p, i) => (
<span
key={i}
dir="ltr"
className="flex items-center rounded-[14px] bg-white/10 px-[20px] py-[12px]"
>
<span className="text-[#E7EEF6] text-[24px] font-semibold leading-none">
{p}
</span>
</span>
))}
</div>
</div>
)}
</div>
)}
</section>
{/* footer */}
<footer className="relative mt-[36px] rounded-[28px] bg-white/8 border border-white/12 p-[32px] flex items-center justify-between">
<div className="flex items-center gap-[24px]">
<div className="rounded-[20px] bg-white p-[16px]">
<QrImg onReady={onQrReady} size={168} />
</div>
<div className="flex flex-col">
<span className="text-white text-[28px] font-bold">
برای رزرو نوبت اسکن کنید
</span>
<span className="text-[#9FC1E6] text-[22px] mt-[8px]" dir="ltr">
{city?.domain || "nobat724.com"}
</span>
</div>
</div>
<div className="flex flex-col items-end gap-[14px]">
<SiteLogo city={city} className="h-[48px] w-[48px]" />
{socialItems.length > 0 && (
<div className="flex items-center gap-[10px]">
{socialItems.map((icon, i) => (
<span
key={i}
className="flex items-center justify-center w-[44px] h-[44px] rounded-full bg-white"
>
{icon}
</span>
))}
</div>
)}
</div>
</footer>
</div>
);
}
export default Poster;