diff --git a/frontend/src/components/insights/ShareBanner.tsx b/frontend/src/components/insights/ShareBanner.tsx index 1015c73..a2e916e 100644 --- a/frontend/src/components/insights/ShareBanner.tsx +++ b/frontend/src/components/insights/ShareBanner.tsx @@ -22,22 +22,47 @@ function formatTimestamp(value: string | null | undefined): string | null { return date.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" }); } -export function ShareBanner({ window, share, isCreating, onCreateShare }: ShareBannerProps) { +export function ShareBanner({ window: insightWindow, share, isCreating, onCreateShare }: ShareBannerProps) { const toast = useToast(); + const browserWindow = + typeof globalThis !== "undefined" && typeof (globalThis as { window?: Window }).window !== "undefined" + ? (globalThis as { window: Window }).window + : null; const generatedLabel = useMemo(() => formatTimestamp(share?.generated_at), [share?.generated_at]); const expiresLabel = useMemo(() => formatTimestamp(share?.cache_expires_at), [share?.cache_expires_at]); + const resolvedShareUrl = useMemo(() => { + if (!share?.url) { + return null; + } + if (!browserWindow) { + return share.url; + } + try { + const currentOrigin = browserWindow.location.origin; + const target = share.url.startsWith("http") ? new URL(share.url) : new URL(share.url, currentOrigin); + if (target.origin !== currentOrigin) { + target.protocol = browserWindow.location.protocol; + target.host = browserWindow.location.host; + } + return target.toString(); + } catch { + return share.url; + } + }, [browserWindow, share?.url]); + const handleCopy = useCallback(async () => { if (!share?.url) { return; } + const url = resolvedShareUrl ?? share.url; try { if (navigator.clipboard && navigator.clipboard.writeText) { - await navigator.clipboard.writeText(share.url); + await navigator.clipboard.writeText(url); } else { const tempInput = document.createElement("input"); - tempInput.value = share.url; + tempInput.value = url; document.body.appendChild(tempInput); tempInput.select(); document.execCommand("copy"); @@ -56,7 +81,7 @@ export function ShareBanner({ window, share, isCreating, onCreateShare }: ShareB variant: "error", }); } - }, [share?.url, toast]); + }, [resolvedShareUrl, share?.url, toast]); return (
@@ -65,7 +90,7 @@ export function ShareBanner({ window, share, isCreating, onCreateShare }: ShareB

Share timeline

Generate a read-only link

- Share the {window === "7d" ? "seven day" : "twenty four hour"} insight snapshot with stakeholders without granting dashboard access. + Share the {insightWindow === "7d" ? "seven day" : "twenty four hour"} insight snapshot with stakeholders without granting dashboard access.

{share ? (
@@ -78,8 +103,8 @@ export function ShareBanner({ window, share, isCreating, onCreateShare }: ShareB
{share ? (
- - {share.url} + + {resolvedShareUrl ?? share.url}