You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

227 lines
7.8 KiB

"use client";
import { useEffect, useState } from "react";
import Button from "@/components/ui/button";
import { useFlutterBridge } from "@/hooks/useFlutterBridge";
const EXIT_ANIMATION_MS = 220;
type ReportActionsSheetProps = {
onClose?: () => void;
};
export function ReportActionsSheet({ onClose }: ReportActionsSheetProps) {
const [isVisible, setIsVisible] = useState(true);
const [isEntering, setIsEntering] = useState(true);
const [isClosing, setIsClosing] = useState(false);
const [showLogs, setShowLogs] = useState(false);
const { sendToFlutter, logs, lastEvent, isReady } = useFlutterBridge({
enableLogging: true,
});
const closeSheet = () => {
if (isClosing) return;
setIsClosing(true);
};
// ✅ دکمه WEB_READY
const handleSendWebReady = () => {
sendToFlutter("WEB_READY", {
timestamp: Date.now(),
url: window.location.href,
userAgent: navigator.userAgent,
});
console.log("✅ WEB_READY ارسال شد");
};
// ✅ دکمه دریافت موقعیت مکانی
// پل اکنون از کانال واقعی HabibApp استفاده می‌کند، پس یک‌بار ارسال کافی است.
const handleGetLocation = () => {
sendToFlutter("REQUEST_LOCATION");
console.log("📍 REQUEST_LOCATION ارسال شد");
};
// ✅ دکمه مشاور
const handleOpenConsultant = () => {
sendToFlutter("REQUEST_CONSULTANT", {
consultant: "habib@gmail.com",
});
console.log("👨‍⚕️ REQUEST_CONSULTANT ارسال شد");
};
useEffect(() => {
const frameId = window.requestAnimationFrame(() => {
setIsEntering(false);
});
return () => window.cancelAnimationFrame(frameId);
}, []);
useEffect(() => {
if (!isVisible) return;
const previousBodyOverflow = document.body.style.overflow;
const previousHtmlOverflow = document.documentElement.style.overflow;
document.body.style.overflow = "hidden";
document.documentElement.style.overflow = "hidden";
return () => {
document.body.style.overflow = previousBodyOverflow;
document.documentElement.style.overflow = previousHtmlOverflow;
};
}, [isVisible]);
useEffect(() => {
if (!isClosing) return;
const timeoutId = window.setTimeout(() => {
setIsVisible(false);
onClose?.();
}, EXIT_ANIMATION_MS);
return () => window.clearTimeout(timeoutId);
}, [isClosing, onClose]);
useEffect(() => {
const handleFlutterResponse: NonNullable<Window["onFlutterResponse"]> = (event) => {
if (event.action === "get_location" && event.success && event.data) {
const message = `Location: ${event.data.latitude}, ${event.data.longitude}`;
alert(message);
}
};
const unsubscribe = window.addFlutterResponseListener?.(handleFlutterResponse);
return () => {
unsubscribe?.();
};
}, []);
if (!isVisible) return null;
return (
<div
className={[
"fixed inset-0 z-50 flex items-end justify-center transition-all duration-[220ms]",
isClosing || isEntering
? "bg-[#171717]/0 opacity-0"
: "bg-[#171717]/55 opacity-100",
]
.filter(Boolean)
.join(" ")}
role="dialog"
aria-modal="true"
onClick={(event) => {
if (event.target === event.currentTarget) closeSheet();
}}
>
<section
className={[
"w-full sm:max-w-[375px] rounded-t-[15px] bg-[#F9F8F8] shadow-[0_20px_60px_rgba(15,23,42,0.08)] transition-transform duration-[220ms] ease-out max-h-[85vh] overflow-y-auto",
isClosing || isEntering ? "translate-y-full" : "translate-y-0",
]
.filter(Boolean)
.join(" ")}
>
{/* Header */}
<div className="sticky top-0 bg-[#F9F8F8] p-3.5 border-b border-gray-200/50">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<h3 className="font-semibold text-gray-800">تنظیمات و پشتیبانی</h3>
<div
className={`w-2 h-2 rounded-full ${isReady ? "bg-green-500" : "bg-gray-400"}`}
title={isReady ? "متصل به Flutter" : "غیرفعال"}
/>
</div>
<button
onClick={closeSheet}
className="text-2xl text-gray-500 hover:text-gray-700 leading-none"
aria-label="بستن"
>
×
</button>
</div>
</div>
<div className="p-3.5 flex flex-col gap-3">
{/* دکمه‌های اصلی */}
<div className="flex flex-col gap-2">
<Button onClick={handleGetLocation}>📍 دریافت موقعیت مکانی</Button>
<Button onClick={handleOpenConsultant}>👨 مشاوره با حبیب</Button>
</div>
{/* دکمه تست WEB_READY */}
<div className="bg-blue-50 rounded-lg p-3 border border-blue-200">
<p className="text-xs text-blue-800 mb-2 font-semibold">
🧪 تست ارتباط با Flutter:
</p>
<Button onClick={handleSendWebReady}>
🚀 ارسال WEB_READY
</Button>
</div>
{/* نمایش آخرین ایونت دریافتی */}
{lastEvent && (
<div className="bg-green-50 rounded-lg p-3 border border-green-200">
<p className="text-xs text-green-800 font-semibold mb-2">
📥 آخرین ایونت از Flutter:
</p>
<div className="bg-white rounded p-2 text-xs font-mono break-all">
<div className="text-green-600 font-bold">
{lastEvent.type}
</div>
<div className="text-gray-600 mt-1">
{JSON.stringify(lastEvent.payload, null, 2)}
</div>
</div>
</div>
)}
{/* لاگ‌ها */}
<div className="bg-gray-50 rounded-lg p-3 border border-gray-200">
<div className="flex items-center justify-between mb-2">
<p className="text-xs text-gray-700 font-semibold">
📋 لاگها ({logs.length})
</p>
<button
onClick={() => setShowLogs(!showLogs)}
className="text-xs text-blue-600 hover:text-blue-800"
>
{showLogs ? "مخفی کردن" : "نمایش"}
</button>
</div>
{showLogs && (
<div className="max-h-[150px] overflow-y-auto space-y-1">
{logs.length === 0 ? (
<p className="text-xs text-gray-500 text-center py-2">
هنوز لاگی ثبت نشده
</p>
) : (
logs.slice(-10).map((log, index) => (
<div
key={index}
className="text-[10px] font-mono bg-white p-1.5 rounded border border-gray-300 break-all"
>
{log}
</div>
))
)}
</div>
)}
</div>
{/* راهنما */}
<div className="bg-yellow-50 rounded-lg p-3 border border-yellow-200">
<p className="text-xs text-yellow-900">
<strong>💡 راهنما:</strong>
<br />
WEB_READY هنگام باز شدن این شیت بهصورت خودکار از کانال
HabibApp ارسال میشود
<br />
نقطهٔ سبز یعنی کانال HabibApp در دسترس است
<br /> برای ارسال دستی، دکمه «ارسال WEB_READY» را بزنید
</p>
</div>
</div>
</section>
</div>
);
}
export default ReportActionsSheet;