import { useLayoutEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import type { IntentBand, Opportunity } from "../../domain/types"; import { useI18n } from "../../i18n/I18nContext"; import { Badge, Button } from "../ui"; import { PrimaryProductPicker } from "./PrimaryProductPicker"; import { ProductMatchDetails } from "./ProductMatchDetails"; import { ReplyComposer } from "./ReplyComposer"; /** 殼層 overflow-x: clip 會把 fixed 鎖在整頁座標;量頂欄/底欄,抽屜才不會衝過頭。 */ function pinDrawerToChrome(el: HTMLElement) { const header = document.querySelector(".hb-shell__header"); const dock = document.querySelector(".hb-dock"); const top = header?.getBoundingClientRect().height ?? 0; const dockHidden = !dock || getComputedStyle(dock).display === "none"; const bottom = dockHidden ? 0 : dock.getBoundingClientRect().height; el.style.top = `${Math.round(top)}px`; el.style.bottom = `${Math.round(bottom)}px`; } const BANDS: IntentBand[] = ["high", "mid", "low"]; type Props = { opportunity: Opportunity; onClose: () => void; onAccept?: (opportunity: Opportunity) => void; onComplete?: (opportunity: Opportunity) => void; /** 改主推產品(多產品匹配時);沒給就不顯示。 */ onSetPrimary?: (opportunity: Opportunity, productId: string, reason: string) => void; /** 覆寫意向分級;沒給就不顯示。 */ onOverrideBand?: (opportunity: Opportunity, band: IntentBand) => void; busy?: boolean; }; export function OpportunityDetailDrawer({ opportunity, onClose, onAccept, onComplete, onSetPrimary, onOverrideBand, busy = false, }: Props) { const { t } = useI18n(); const [overrideOpen, setOverrideOpen] = useState(false); const drawerRef = useRef(null); const pending = (opportunity.review_state || "pending") === "pending"; const accepted = opportunity.status === "accepted" || Boolean(opportunity.contact_id); const matches = opportunity.product_matches ?? []; useLayoutEffect(() => { const el = drawerRef.current; if (!el) return; const apply = () => pinDrawerToChrome(el); apply(); const header = document.querySelector(".hb-shell__header"); const dock = document.querySelector(".hb-dock"); const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(apply); if (ro && header) ro.observe(header); if (ro && dock) ro.observe(dock); window.addEventListener("resize", apply); return () => { ro?.disconnect(); window.removeEventListener("resize", apply); }; }, []); const drawer = ( ); return createPortal(drawer, document.body); }