// components/ShareLinkButton.tsx // Renders the public onboarding URL with a native-share / copy-to-clipboard // affordance. On mobile we use navigator.share() so the user can fire the // link off via WhatsApp / SMS / email / any installed share target. On // desktop (or any browser without navigator.share) we fall back to a // "Copy link" button. After success we show a transient confirmation // ("Copied" / "Sent") for ~2s and then revert to the original label. "use client"; import { useEffect, useState } from "react"; type ShareState = "idle" | "copied" | "shared" | "error"; interface Props { /** The full URL the user should send to the client. */ url: string; /** Optional share-sheet title. Defaults to "Workflow onboarding link". */ title?: string; /** Optional one-liner that ships in the share sheet body where * the platform supports it. */ text?: string; } export default function ShareLinkButton({ url, title = "Workflow onboarding link", text = "Please complete your company and director information using this secure link:" }: Props) { const [state, setState] = useState("idle"); const [canNativeShare, setCanNativeShare] = useState(false); useEffect(() => { // navigator.share is mobile-first; some desktop browsers have it // behind a flag. We render the native button optimistically when // present (the type narrowing avoids SSR-mismatch issues since // we only set the flag in an effect). if ( typeof navigator !== "undefined" && typeof navigator.share === "function" ) { setCanNativeShare(true); } }, []); async function copyToClipboard() { try { // Modern path with Clipboard API; falls back to the legacy // textarea-trick if the secure API isn't allowed (HTTP context). if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(url); } else { const ta = document.createElement("textarea"); ta.value = url; ta.setAttribute("readonly", ""); ta.style.position = "fixed"; ta.style.opacity = "0"; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); document.body.removeChild(ta); } setState("copied"); } catch { setState("error"); } window.setTimeout(() => setState("idle"), 1800); } async function shareNative() { try { await navigator.share({ url, title, text }); setState("shared"); } catch (err) { // User-cancelled share is NOT an error — silent revert. if ((err as DOMException)?.name === "AbortError") { setState("idle"); return; } setState("error"); } window.setTimeout(() => setState("idle"), 1800); } const label = state === "copied" ? "Copied ✓" : state === "shared" ? "Sent ✓" : state === "error" ? "Try again" : canNativeShare ? "Share link" : "Copy link"; return (
Open link ↗
); } function IconShare({ className }: { className?: string }) { return ( ); }