diff --git a/src/App.tsx b/src/App.tsx index 53704f3b..23da0412 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -24,6 +24,7 @@ import { ResilienceProvider } from "@/components/layout/ResilienceProvider"; import { useAdminRoutePreload } from "@/hooks/useAdminRoutePreload"; import { useVersionCheck } from "@/hooks/useVersionCheck"; import { cn } from "@/lib/utils"; +import { PageTransition } from "@/components/layout/PageTransition"; // Core routes (eager-loaded for best UX) import Index from "./pages/Index"; @@ -164,8 +165,9 @@ function AppContent(): React.JSX.Element {
}> - - + + + {/* Core routes - eager loaded */} } /> } /> @@ -443,7 +445,8 @@ function AppContent(): React.JSX.Element { } /> - + +
diff --git a/src/components/layout/PageTransition.tsx b/src/components/layout/PageTransition.tsx new file mode 100644 index 00000000..97a3fc7c --- /dev/null +++ b/src/components/layout/PageTransition.tsx @@ -0,0 +1,34 @@ +import { ReactNode, useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; + +interface PageTransitionProps { + children: ReactNode; +} + +export function PageTransition({ children }: PageTransitionProps) { + const location = useLocation(); + const [displayLocation, setDisplayLocation] = useState(location); + const [transitionStage, setTransitionStage] = useState<'fade-in' | 'fade-out'>('fade-in'); + + useEffect(() => { + if (location !== displayLocation) { + setTransitionStage('fade-out'); + } + }, [location, displayLocation]); + + const onAnimationEnd = () => { + if (transitionStage === 'fade-out') { + setTransitionStage('fade-in'); + setDisplayLocation(location); + } + }; + + return ( +
+ {children} +
+ ); +} diff --git a/src/components/navigation/EntityBreadcrumb.tsx b/src/components/navigation/EntityBreadcrumb.tsx new file mode 100644 index 00000000..fec24eaf --- /dev/null +++ b/src/components/navigation/EntityBreadcrumb.tsx @@ -0,0 +1,87 @@ +import { Link } from 'react-router-dom'; +import { Home } from 'lucide-react'; +import { + Breadcrumb, + BreadcrumbList, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbPage, + BreadcrumbSeparator, +} from '@/components/ui/breadcrumb'; +import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'; +import { ParkPreviewCard } from '@/components/preview/ParkPreviewCard'; +import { CompanyPreviewCard } from '@/components/preview/CompanyPreviewCard'; + +interface BreadcrumbSegment { + label: string; + href?: string; + showPreview?: boolean; + previewType?: 'park' | 'company'; + previewSlug?: string; +} + +interface EntityBreadcrumbProps { + segments: BreadcrumbSegment[]; + className?: string; +} + +export function EntityBreadcrumb({ segments, className }: EntityBreadcrumbProps) { + return ( + + + {/* Home link */} + + + + + Home + + + + + {segments.map((segment, index) => { + const isLast = index === segments.length - 1; + + return ( + + + {isLast ? ( + {segment.label} + ) : segment.showPreview && segment.previewSlug ? ( + + + + + {segment.label} + + + + + {segment.previewType === 'park' && ( + + )} + {segment.previewType === 'company' && ( + + )} + + + ) : ( + + + {segment.label} + + + )} + + ); + })} + + + ); +} diff --git a/src/pages/DesignerDetail.tsx b/src/pages/DesignerDetail.tsx index 3322d61a..5b193d45 100644 --- a/src/pages/DesignerDetail.tsx +++ b/src/pages/DesignerDetail.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, lazy, Suspense } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; +import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb'; import { Header } from '@/components/layout/Header'; import { getBannerUrls } from '@/lib/cloudflareImageUtils'; import { Button } from '@/components/ui/button'; @@ -181,6 +182,15 @@ export default function DesignerDetail() {
+ {/* Breadcrumb Navigation */} + + {/* Back Button and Edit Button */}