"use client" import * as React from "react" import { Navigate, Outlet, useParams } from "react-router" import { useAppStore } from "@exxatdesignux/product-framework/app-store" import { resolveProductFromPath } from "@exxatdesignux/product-framework/product-routing" import { usePersistHydrated } from "../../hooks/use-persist-hydrated" import { useShippedCatalogReady } from "../../hooks/use-shipped-catalog-ready" export interface ProductRootGateProps { /** * Rendered while the persisted store is hydrating from localStorage. * Defaults to `null` (renders nothing). Pass a route-shaped skeleton * (e.g. ``) for a smoother first paint. */ loadingFallback?: React.ReactNode /** * Where to redirect when the URL segment doesn't resolve to a registered * product. Defaults to `/prism/dashboard`. Customer apps can override * (e.g. `//dashboard`). */ fallbackPath?: string } /** * Validates `:productRootSegment` against built-in slugs and configured * custom products. Unknown segments bounce to `fallbackPath`; known * segments render the child route via ``. * * Mount as the `element` of a `:productRootSegment` parent route: * * ```tsx * }> * } /> * … * * ``` */ export function ProductRootGate({ loadingFallback = null, fallbackPath = "/prism/dashboard", }: ProductRootGateProps = {}) { const { productRootSegment = "" } = useParams() const customProducts = useAppStore(s => s.customProducts) const hydrated = usePersistHydrated() const shippedReady = useShippedCatalogReady() const resolved = resolveProductFromPath(`/${productRootSegment}`, customProducts) if (resolved) { return } // Built-in product slugs resolve without persisted custom product state. // For unknown segments, wait until hydration + shipped catalog load finish // before redirecting so tenant suffixes resolve from store or deploy JSON. if (!hydrated || !shippedReady) { return <>{loadingFallback} } return }