()
| 57 | ]; |
| 58 | |
| 59 | export default function TipsPanel() { |
| 60 | const [open, setOpen] = useState(false); |
| 61 | const [copied, setCopied] = useState(false); |
| 62 | const [toastFading, setToastFading] = useState(false); |
| 63 | |
| 64 | useEffect(() => { |
| 65 | if (!open) return; |
| 66 | const handleKey = (e: KeyboardEvent) => { |
| 67 | if (e.key === "Escape") { setOpen(false); trackEvent("panel_close", { panel: "tips" }); } |
| 68 | }; |
| 69 | window.addEventListener("keydown", handleKey); |
| 70 | return () => window.removeEventListener("keydown", handleKey); |
| 71 | }, [open]); |
| 72 | |
| 73 | const copyToClipboard = useCallback(() => { |
| 74 | const text = tipGroups |
| 75 | .map((group) => { |
| 76 | const rows = group.tips |
| 77 | .map((tip) => ` ${tip.condition} → ${tip.approaches.join(", ")}`) |
| 78 | .join("\n"); |
| 79 | return `${group.label}\n${rows}`; |
| 80 | }) |
| 81 | .join("\n\n"); |
| 82 | navigator.clipboard.writeText(text); |
| 83 | setCopied(true); |
| 84 | setToastFading(false); |
| 85 | trackEvent("copy_tips"); |
| 86 | }, []); |
| 87 | |
| 88 | useEffect(() => { |
| 89 | if (!copied) return; |
| 90 | const fadeTimer = setTimeout(() => setToastFading(true), 1500); |
| 91 | const removeTimer = setTimeout(() => { setCopied(false); setToastFading(false); }, 2200); |
| 92 | return () => { clearTimeout(fadeTimer); clearTimeout(removeTimer); }; |
| 93 | }, [copied]); |
| 94 | |
| 95 | return ( |
| 96 | <> |
| 97 | {/* Tab button – rendered inline inside the fixed flex wrapper in page.tsx */} |
| 98 | <button |
| 99 | onClick={() => { setOpen(true); trackEvent("panel_open", { panel: "tips" }); }} |
| 100 | className="rounded-r-xl bg-blue-600 px-2.5 py-4 text-white shadow-lg transition-colors hover:bg-blue-700" |
| 101 | aria-label="Open tips" |
| 102 | > |
| 103 | <span className="flex items-center gap-2 text-sm font-semibold [writing-mode:vertical-lr]"> |
| 104 | <Lightbulb className="h-4 w-4 rotate-90" /> |
| 105 | Helpful Tips |
| 106 | </span> |
| 107 | </button> |
| 108 | |
| 109 | {/* Panel */} |
| 110 | <div |
| 111 | className={`fixed left-0 top-0 z-50 h-full w-80 transform border-r border-zinc-200 bg-white shadow-xl transition-transform duration-300 ease-in-out dark:border-zinc-700 dark:bg-zinc-900 ${ |
| 112 | open ? "translate-x-0" : "-translate-x-full" |
| 113 | }`} |
| 114 | > |
| 115 | <div className="flex items-center justify-between bg-blue-600 px-4 py-3 text-white"> |
| 116 | <h2 className="flex items-center gap-2 text-base font-semibold"> |
nothing calls this directly
no test coverage detected