- Appointment detail (DetailLg/DetailSm) read the real response shape: date/time from slot_start, specialty from doctor.specialties[0].name, phone from address.telephone. - Sidebar Head reads userInfo (cookie) after mount to avoid an SSR/client hydration mismatch on the user's name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
125 lines
4.9 KiB
JavaScript
125 lines
4.9 KiB
JavaScript
import RoutingC from "@/components/icons/RoutingC";
|
|
import { Button } from "@mui/material";
|
|
import React, { useRef } from "react";
|
|
import ButtonData from "./ButtonData";
|
|
import TextLoading from "@/app/component/loading/Text";
|
|
import { convertTimestampToJalali, convertTimestampToTime } from "@/helper";
|
|
import html2canvas from "html2canvas";
|
|
import jsPDF from "jspdf";
|
|
|
|
function DetailSm({ appointmentData, loading }) {
|
|
const detailsRef = useRef(null);
|
|
|
|
const handleDownloadPDF = async () => {
|
|
if (!detailsRef.current) return;
|
|
|
|
try {
|
|
const canvas = await html2canvas(detailsRef.current, {
|
|
scale: 2,
|
|
useCORS: true,
|
|
logging: false,
|
|
backgroundColor: '#ffffff',
|
|
});
|
|
|
|
const imgData = canvas.toDataURL("image/png");
|
|
const pdf = new jsPDF({
|
|
orientation: "portrait",
|
|
unit: "mm",
|
|
format: "a4",
|
|
});
|
|
|
|
const pdfWidth = pdf.internal.pageSize.getWidth();
|
|
const pdfHeight = pdf.internal.pageSize.getHeight();
|
|
|
|
const imgWidth = pdfWidth * 0.9; // 90% of page width
|
|
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
|
|
|
// Center horizontally and vertically
|
|
const xPosition = (pdfWidth - imgWidth) / 2;
|
|
const yPosition = (pdfHeight - imgHeight) / 2;
|
|
|
|
pdf.addImage(imgData, "PNG", xPosition, yPosition, imgWidth, imgHeight);
|
|
pdf.save(`appointment_${appointmentData?.uuid || Date.now()}.pdf`);
|
|
} catch (error) {
|
|
console.error("Error generating PDF:", error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div ref={detailsRef} className="mt-[20px] block md:hidden">
|
|
<div className="flex items-center justify-start gap-[8px]">
|
|
<div className="flex flex-col justify-center items-start gap-[8px]">
|
|
<TextLoading loading={loading} width={60} height={14}>
|
|
<p className="text-[#616161] text-[14px] font-medium">
|
|
{appointmentData?.doctor?.name}
|
|
</p>
|
|
</TextLoading>
|
|
<TextLoading loading={loading} width={70} height={14}>
|
|
<p className="text-[#7E7E7E] text-[14px] font-normal">
|
|
{appointmentData?.doctor?.specialties?.[0]?.name}
|
|
</p>
|
|
</TextLoading>
|
|
</div>
|
|
</div>
|
|
<ul className="flex w-full flex-col items-start justify-start mt-[16px]">
|
|
<li className="flex items-center w-full justify-between">
|
|
<TextLoading loading={loading} width={70} height={16}>
|
|
<p className="text-[#7E7E7E] text-[16px] font-normal">
|
|
تاریخ نوبت:
|
|
</p>
|
|
</TextLoading>
|
|
<TextLoading loading={loading} width={55} height={16}>
|
|
<p className="text-[#525252] text-[16px] font-medium">
|
|
{convertTimestampToJalali(appointmentData?.slot_start)}
|
|
</p>
|
|
</TextLoading>
|
|
</li>
|
|
<span className="block w-full h-px bg-[#EFEFEF] mt-[12px] mb-[16px]"></span>
|
|
<li className="flex flex-wrap items-center w-full justify-between">
|
|
<TextLoading loading={loading} width={80} height={16}>
|
|
<p className="text-[#7E7E7E] text-[16px] font-normal">ساعت نوبت:</p>
|
|
</TextLoading>
|
|
<TextLoading loading={loading} width={60} height={16}>
|
|
<p className="text-[#525252] text-[16px] font-medium">
|
|
{convertTimestampToTime(appointmentData?.slot_start)}
|
|
</p>
|
|
</TextLoading>
|
|
</li>
|
|
<span className="block w-full h-px bg-[#EFEFEF] mt-[12px] mb-[16px]"></span>
|
|
<li className="flex flex-wrap gap-x-[12px] gap-y-[12px] items-center w-full justify-between">
|
|
<TextLoading loading={loading} width={200} height={16}>
|
|
<p className="text-[#7E7E7E] text-[14px] font-normal">
|
|
آدرس:<span className="text-[#616161]">{appointmentData?.address?.address}</span>
|
|
</p>
|
|
</TextLoading>
|
|
<TextLoading loading={loading} width={130} height={40}>
|
|
<Button
|
|
className="!py-[8px] !border-[#EFEFEF] !px-[12px] !gap-[8px]"
|
|
variant="outlined"
|
|
onClick={() => {
|
|
const map = appointmentData?.address?.map;
|
|
if (map?.latitude && map?.longitude) {
|
|
window.open(`https://www.google.com/maps/dir/?api=1&destination=${map.latitude},${map.longitude}`, '_blank');
|
|
}
|
|
}}
|
|
>
|
|
<RoutingC />
|
|
<p className="text-[#5559CE] text-[14px] font-medium">
|
|
مسیریابی آدرس
|
|
</p>
|
|
</Button>
|
|
</TextLoading>
|
|
</li>
|
|
<TextLoading loading={loading} width={100} height={18}>
|
|
<p className="text-[#7E7E7E] text-[14px] font-normal mt-[12px]">
|
|
تلفن: <span className="text-[#616161]">{appointmentData?.address?.telephone}</span>
|
|
</p>
|
|
</TextLoading>
|
|
</ul>
|
|
<ButtonData loading={loading} onDownload={handleDownloadPDF} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default DetailSm;
|