Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Introduced turbopack alias for canvas in next.config.mjs. - Updated package.json scripts for development and branding tasks. - Added new dependencies for Tiptap extensions. - Implemented new demo layouts for agenda, contacts, drive, and mail applications. - Enhanced globals.css for improved theming and splash screen animations. - Added OAuth callback handling for drive mounts. - Updated layout components to integrate new demo shells and improve structure.
86 lines
2.3 KiB
TypeScript
86 lines
2.3 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect } from "react"
|
|
import { usePathname } from "next/navigation"
|
|
import { toast } from "sonner"
|
|
import {
|
|
getDemoRouteRoot,
|
|
isHrefWithinDemoScope,
|
|
} from "@/lib/demo/demo-navigation"
|
|
|
|
const DEMO_NAV_TOAST_ID = "demo-nav-blocked"
|
|
|
|
function showDemoNavBlockedToast() {
|
|
toast.message("Navigation hors démo", {
|
|
id: DEMO_NAV_TOAST_ID,
|
|
description:
|
|
"Créez un compte pour accéder à toute la suite — la démo reste sur cette application.",
|
|
})
|
|
}
|
|
|
|
function urlArgToHref(url: string | URL | null | undefined): string | null {
|
|
if (url == null || url === "") return null
|
|
return typeof url === "string" ? url : url.toString()
|
|
}
|
|
|
|
/** Bloque liens et router client hors du scope `/demo/<app>/…`. */
|
|
export function DemoNavigationGuard() {
|
|
const pathname = usePathname()
|
|
const demoRouteRoot = getDemoRouteRoot(pathname)
|
|
|
|
useEffect(() => {
|
|
if (!demoRouteRoot) return
|
|
|
|
const origin = window.location.origin
|
|
|
|
const isAllowed = (href: string | null) => {
|
|
if (!href) return true
|
|
return isHrefWithinDemoScope(href, demoRouteRoot, origin)
|
|
}
|
|
|
|
const originalPushState = history.pushState.bind(history)
|
|
const originalReplaceState = history.replaceState.bind(history)
|
|
|
|
history.pushState = (state, title, url) => {
|
|
const href = urlArgToHref(url)
|
|
if (!isAllowed(href)) {
|
|
showDemoNavBlockedToast()
|
|
return
|
|
}
|
|
return originalPushState(state, title, url)
|
|
}
|
|
|
|
history.replaceState = (state, title, url) => {
|
|
const href = urlArgToHref(url)
|
|
if (!isAllowed(href)) {
|
|
showDemoNavBlockedToast()
|
|
return
|
|
}
|
|
return originalReplaceState(state, title, url)
|
|
}
|
|
|
|
const handleClick = (event: MouseEvent) => {
|
|
const anchor = (event.target as Element).closest("a[href]")
|
|
if (!anchor) return
|
|
if (anchor.getAttribute("target") === "_blank") return
|
|
|
|
const href = anchor.getAttribute("href")
|
|
if (!href || isAllowed(href)) return
|
|
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
showDemoNavBlockedToast()
|
|
}
|
|
|
|
document.addEventListener("click", handleClick, true)
|
|
|
|
return () => {
|
|
history.pushState = originalPushState
|
|
history.replaceState = originalReplaceState
|
|
document.removeEventListener("click", handleClick, true)
|
|
}
|
|
}, [demoRouteRoot])
|
|
|
|
return null
|
|
}
|