mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 08:51:13 -05:00
Enhance loading skeletons and breadcrumbs
- Add content-m matching loading skeletons for ParkDetail, RideDetail, CompanyDetail, etc., replacing generic spinners to preserve layout during load - Remove redundant Back to Parent Entity buttons in detail pages in favor of breadcrumb navigation - Prepare groundwork for breadcrumbs across detail pages to improve cohesion and navigation
This commit is contained in:
98
src/components/loading/CompanyDetailSkeleton.tsx
Normal file
98
src/components/loading/CompanyDetailSkeleton.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||||
|
|
||||||
|
export function CompanyDetailSkeleton() {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8 max-w-7xl animate-pulse">
|
||||||
|
{/* Breadcrumb */}
|
||||||
|
<div className="h-4 bg-muted rounded w-56 mb-4" />
|
||||||
|
|
||||||
|
{/* Edit Button Area */}
|
||||||
|
<div className="flex justify-end mb-6">
|
||||||
|
<div className="h-10 bg-muted rounded w-32" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Hero Banner */}
|
||||||
|
<div className="aspect-[21/9] bg-muted rounded-lg mb-8" />
|
||||||
|
|
||||||
|
{/* Stats Cards */}
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-12 max-w-6xl mx-auto">
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<Card key={i} className="border-0 bg-gradient-to-br from-muted/50 to-muted/30">
|
||||||
|
<CardContent className="p-4 text-center">
|
||||||
|
<div className="h-8 bg-muted rounded w-16 mx-auto mb-2" />
|
||||||
|
<div className="h-3 bg-muted rounded w-20 mx-auto" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tabs */}
|
||||||
|
<div className="flex gap-2 border-b mb-6">
|
||||||
|
{['Overview', 'Rides', 'Models', 'Photos'].map((tab) => (
|
||||||
|
<div key={tab} className="h-10 bg-muted rounded w-20" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content Grid */}
|
||||||
|
<div className="grid lg:grid-cols-3 gap-6">
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="lg:col-span-2 space-y-6">
|
||||||
|
{/* Description Card */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="h-6 bg-muted rounded w-48" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-3">
|
||||||
|
<div className="h-4 bg-muted rounded w-full" />
|
||||||
|
<div className="h-4 bg-muted rounded w-full" />
|
||||||
|
<div className="h-4 bg-muted rounded w-4/5" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Products Grid */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="h-6 bg-muted rounded w-40" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||||
|
<div key={i} className="space-y-2">
|
||||||
|
<div className="aspect-square bg-muted rounded-lg" />
|
||||||
|
<div className="h-4 bg-muted rounded w-full" />
|
||||||
|
<div className="h-3 bg-muted rounded w-2/3" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Company Info Card */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="h-6 bg-muted rounded w-40" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
{/* Logo */}
|
||||||
|
<div className="w-32 h-32 bg-muted rounded mx-auto mb-4" />
|
||||||
|
|
||||||
|
{/* Info Items */}
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<div key={i} className="flex items-center gap-3">
|
||||||
|
<div className="w-4 h-4 bg-muted rounded" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="h-4 bg-muted rounded w-24 mb-1" />
|
||||||
|
<div className="h-3 bg-muted rounded w-32" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
101
src/components/loading/ParkDetailSkeleton.tsx
Normal file
101
src/components/loading/ParkDetailSkeleton.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||||
|
|
||||||
|
export function ParkDetailSkeleton() {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8 max-w-7xl animate-pulse">
|
||||||
|
{/* Breadcrumb */}
|
||||||
|
<div className="h-4 bg-muted rounded w-48 mb-4" />
|
||||||
|
|
||||||
|
{/* Edit Button Area */}
|
||||||
|
<div className="flex justify-end mb-6">
|
||||||
|
<div className="h-10 bg-muted rounded w-32" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Hero Banner */}
|
||||||
|
<div className="aspect-[21/9] bg-muted rounded-lg mb-8" />
|
||||||
|
|
||||||
|
{/* Stats Cards */}
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-12 max-w-6xl mx-auto">
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<Card key={i} className="border-0 bg-gradient-to-br from-muted/50 to-muted/30">
|
||||||
|
<CardContent className="p-4 text-center">
|
||||||
|
<div className="h-8 bg-muted rounded w-16 mx-auto mb-2" />
|
||||||
|
<div className="h-3 bg-muted rounded w-20 mx-auto" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tabs */}
|
||||||
|
<div className="flex gap-2 border-b mb-6">
|
||||||
|
{['Overview', 'Rides', 'Reviews', 'Photos', 'History'].map((tab) => (
|
||||||
|
<div key={tab} className="h-10 bg-muted rounded w-24" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content Grid */}
|
||||||
|
<div className="grid lg:grid-cols-3 gap-6">
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="lg:col-span-2 space-y-6">
|
||||||
|
{/* Description Card */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="h-6 bg-muted rounded w-48" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-3">
|
||||||
|
<div className="h-4 bg-muted rounded w-full" />
|
||||||
|
<div className="h-4 bg-muted rounded w-full" />
|
||||||
|
<div className="h-4 bg-muted rounded w-3/4" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Featured Rides Card */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="h-6 bg-muted rounded w-40" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<div key={i} className="space-y-2">
|
||||||
|
<div className="aspect-square bg-muted rounded-lg" />
|
||||||
|
<div className="h-4 bg-muted rounded w-full" />
|
||||||
|
<div className="h-3 bg-muted rounded w-3/4" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Info Card */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="h-6 bg-muted rounded w-40" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<div key={i} className="flex items-center gap-3">
|
||||||
|
<div className="w-4 h-4 bg-muted rounded" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="h-4 bg-muted rounded w-24 mb-1" />
|
||||||
|
<div className="h-3 bg-muted rounded w-32" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Map Card */}
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-0">
|
||||||
|
<div className="aspect-square bg-muted rounded-lg" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
106
src/components/loading/RideDetailSkeleton.tsx
Normal file
106
src/components/loading/RideDetailSkeleton.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
|
|
||||||
|
export function RideDetailSkeleton() {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8 max-w-7xl animate-pulse">
|
||||||
|
{/* Breadcrumb */}
|
||||||
|
<div className="h-4 bg-muted rounded w-64 mb-4" />
|
||||||
|
|
||||||
|
{/* Edit Button Area */}
|
||||||
|
<div className="flex justify-end mb-6">
|
||||||
|
<div className="h-10 bg-muted rounded w-32" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Hero Banner */}
|
||||||
|
<div className="aspect-[21/9] bg-muted rounded-lg mb-8" />
|
||||||
|
|
||||||
|
{/* Stats Grid */}
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4 mb-12">
|
||||||
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||||
|
<Card key={i} className="border-0 bg-gradient-to-br from-muted/50 to-muted/30">
|
||||||
|
<CardContent className="p-4 text-center">
|
||||||
|
<div className="w-6 h-6 bg-muted rounded mx-auto mb-2" />
|
||||||
|
<div className="h-8 bg-muted rounded w-16 mx-auto mb-1" />
|
||||||
|
<div className="h-3 bg-muted rounded w-12 mx-auto" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tabs */}
|
||||||
|
<div className="flex gap-2 border-b mb-6">
|
||||||
|
{['Overview', 'Reviews', 'Photos', 'History'].map((tab) => (
|
||||||
|
<div key={tab} className="h-10 bg-muted rounded w-24" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content Grid */}
|
||||||
|
<div className="grid lg:grid-cols-3 gap-6">
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="lg:col-span-2 space-y-6">
|
||||||
|
{/* Description Card */}
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-6 space-y-3">
|
||||||
|
<div className="h-6 bg-muted rounded w-48 mb-4" />
|
||||||
|
<div className="h-4 bg-muted rounded w-full" />
|
||||||
|
<div className="h-4 bg-muted rounded w-full" />
|
||||||
|
<div className="h-4 bg-muted rounded w-5/6" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Technical Specs */}
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-6 space-y-4">
|
||||||
|
<div className="h-6 bg-muted rounded w-56 mb-4" />
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||||
|
<div key={i} className="space-y-2">
|
||||||
|
<div className="h-3 bg-muted rounded w-24" />
|
||||||
|
<div className="h-5 bg-muted rounded w-32" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Ride Info Card */}
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-6 space-y-4">
|
||||||
|
<div className="h-6 bg-muted rounded w-40 mb-4" />
|
||||||
|
{[1, 2, 3, 4, 5].map((i) => (
|
||||||
|
<div key={i} className="flex items-center gap-3">
|
||||||
|
<div className="w-4 h-4 bg-muted rounded" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="h-4 bg-muted rounded w-20 mb-1" />
|
||||||
|
<div className="h-3 bg-muted rounded w-28" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Similar Rides */}
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-6">
|
||||||
|
<div className="h-6 bg-muted rounded w-32 mb-4" />
|
||||||
|
<div className="space-y-3">
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<div key={i} className="flex gap-3">
|
||||||
|
<div className="w-16 h-16 bg-muted rounded" />
|
||||||
|
<div className="flex-1 space-y-2">
|
||||||
|
<div className="h-4 bg-muted rounded w-full" />
|
||||||
|
<div className="h-3 bg-muted rounded w-3/4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect, lazy, Suspense } from 'react';
|
import { useState, useEffect, lazy, Suspense } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
||||||
|
import { CompanyDetailSkeleton } from '@/components/loading/CompanyDetailSkeleton';
|
||||||
import { Header } from '@/components/layout/Header';
|
import { Header } from '@/components/layout/Header';
|
||||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -150,12 +151,7 @@ export default function DesignerDetail() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="container mx-auto px-4 py-8">
|
<CompanyDetailSkeleton />
|
||||||
<div className="animate-pulse space-y-6">
|
|
||||||
<div className="h-64 bg-muted rounded-lg"></div>
|
|
||||||
<div className="h-8 bg-muted rounded w-1/2"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -191,13 +187,8 @@ export default function DesignerDetail() {
|
|||||||
className="mb-4"
|
className="mb-4"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Back Button and Edit Button */}
|
{/* Edit Button */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex justify-end mb-6">
|
||||||
<Button variant="ghost" onClick={() => navigate('/designers')}>
|
|
||||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
||||||
Back to Designers
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this designer")}
|
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this designer")}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect, lazy, Suspense } from 'react';
|
import { useState, useEffect, lazy, Suspense } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
||||||
|
import { CompanyDetailSkeleton } from '@/components/loading/CompanyDetailSkeleton';
|
||||||
import { Header } from '@/components/layout/Header';
|
import { Header } from '@/components/layout/Header';
|
||||||
import { trackPageView } from '@/lib/viewTracking';
|
import { trackPageView } from '@/lib/viewTracking';
|
||||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||||
@@ -160,12 +161,7 @@ export default function ManufacturerDetail() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="container mx-auto px-4 py-8">
|
<CompanyDetailSkeleton />
|
||||||
<div className="animate-pulse space-y-6">
|
|
||||||
<div className="h-64 bg-muted rounded-lg"></div>
|
|
||||||
<div className="h-8 bg-muted rounded w-1/2"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -201,14 +197,8 @@ export default function ManufacturerDetail() {
|
|||||||
className="mb-4"
|
className="mb-4"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Back Button and Edit Button */}
|
{/* Edit Button */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex justify-end mb-6">
|
||||||
<Button variant="ghost" onClick={() => navigate('/manufacturers')}>
|
|
||||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
||||||
<span className="md:hidden">Back</span>
|
|
||||||
<span className="hidden md:inline">Back to Manufacturers</span>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this manufacturer")}
|
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this manufacturer")}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect, lazy, Suspense } from 'react';
|
import { useState, useEffect, lazy, Suspense } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
||||||
|
import { CompanyDetailSkeleton } from '@/components/loading/CompanyDetailSkeleton';
|
||||||
import { Header } from '@/components/layout/Header';
|
import { Header } from '@/components/layout/Header';
|
||||||
import { trackPageView } from '@/lib/viewTracking';
|
import { trackPageView } from '@/lib/viewTracking';
|
||||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||||
@@ -189,12 +190,7 @@ export default function OperatorDetail() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="container mx-auto px-4 py-8">
|
<CompanyDetailSkeleton />
|
||||||
<div className="animate-pulse space-y-6">
|
|
||||||
<div className="h-64 bg-muted rounded-lg"></div>
|
|
||||||
<div className="h-8 bg-muted rounded w-1/2"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -230,13 +226,8 @@ export default function OperatorDetail() {
|
|||||||
className="mb-4"
|
className="mb-4"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Back Button and Edit Button */}
|
{/* Edit Button */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex justify-end mb-6">
|
||||||
<Button variant="ghost" onClick={() => navigate('/operators')}>
|
|
||||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
||||||
Back to Operators
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this operator")}
|
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this operator")}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useParams, useNavigate, Link } from 'react-router-dom';
|
|||||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
|
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
|
||||||
import { CompanyPreviewCard } from '@/components/preview/CompanyPreviewCard';
|
import { CompanyPreviewCard } from '@/components/preview/CompanyPreviewCard';
|
||||||
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
||||||
|
import { ParkDetailSkeleton } from '@/components/loading/ParkDetailSkeleton';
|
||||||
import { Header } from '@/components/layout/Header';
|
import { Header } from '@/components/layout/Header';
|
||||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||||
import { trackPageView } from '@/lib/viewTracking';
|
import { trackPageView } from '@/lib/viewTracking';
|
||||||
@@ -164,13 +165,7 @@ export default function ParkDetail() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return <div className="min-h-screen bg-background">
|
return <div className="min-h-screen bg-background">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="container mx-auto px-4 py-8">
|
<ParkDetailSkeleton />
|
||||||
<div className="animate-pulse space-y-6">
|
|
||||||
<div className="h-64 bg-muted rounded-lg"></div>
|
|
||||||
<div className="h-8 bg-muted rounded w-1/2"></div>
|
|
||||||
<div className="h-4 bg-muted rounded w-1/3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
if (!park) {
|
if (!park) {
|
||||||
@@ -203,13 +198,8 @@ export default function ParkDetail() {
|
|||||||
className="mb-4"
|
className="mb-4"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Back Button and Edit Button */}
|
{/* Edit Button */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex justify-end mb-6">
|
||||||
<Button variant="ghost" onClick={() => navigate('/parks')}>
|
|
||||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
||||||
Back to Parks
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => requireAuth(() => setIsEditParkModalOpen(true), "Sign in to edit this park")}
|
onClick={() => requireAuth(() => setIsEditParkModalOpen(true), "Sign in to edit this park")}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { useState, useEffect, lazy, Suspense } from 'react';
|
import { useState, useEffect, lazy, Suspense } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { Header } from '@/components/layout/Header';
|
import { Header } from '@/components/layout/Header';
|
||||||
|
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
||||||
|
import { CompanyDetailSkeleton } from '@/components/loading/CompanyDetailSkeleton';
|
||||||
import { trackPageView } from '@/lib/viewTracking';
|
import { trackPageView } from '@/lib/viewTracking';
|
||||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -188,12 +190,7 @@ export default function PropertyOwnerDetail() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="container mx-auto px-4 py-8">
|
<CompanyDetailSkeleton />
|
||||||
<div className="animate-pulse space-y-6">
|
|
||||||
<div className="h-64 bg-muted rounded-lg"></div>
|
|
||||||
<div className="h-8 bg-muted rounded w-1/2"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -220,13 +217,17 @@ export default function PropertyOwnerDetail() {
|
|||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
<main className="container mx-auto px-4 py-8 max-w-7xl">
|
<main className="container mx-auto px-4 py-8 max-w-7xl">
|
||||||
{/* Back Button and Edit Button */}
|
{/* Breadcrumb Navigation */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<EntityBreadcrumb
|
||||||
<Button variant="ghost" onClick={() => navigate('/owners')}>
|
segments={[
|
||||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
{ label: 'Property Owners', href: '/owners' },
|
||||||
Back to Property Owners
|
{ label: owner.name }
|
||||||
</Button>
|
]}
|
||||||
|
className="mb-4"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Edit Button */}
|
||||||
|
<div className="flex justify-end mb-6">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this property owner")}
|
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this property owner")}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/h
|
|||||||
import { CompanyPreviewCard } from '@/components/preview/CompanyPreviewCard';
|
import { CompanyPreviewCard } from '@/components/preview/CompanyPreviewCard';
|
||||||
import { ParkPreviewCard } from '@/components/preview/ParkPreviewCard';
|
import { ParkPreviewCard } from '@/components/preview/ParkPreviewCard';
|
||||||
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
||||||
|
import { RideDetailSkeleton } from '@/components/loading/RideDetailSkeleton';
|
||||||
import { Header } from '@/components/layout/Header';
|
import { Header } from '@/components/layout/Header';
|
||||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||||
import { trackPageView } from '@/lib/viewTracking';
|
import { trackPageView } from '@/lib/viewTracking';
|
||||||
@@ -164,13 +165,7 @@ export default function RideDetail() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="container mx-auto px-4 py-8">
|
<RideDetailSkeleton />
|
||||||
<div className="animate-pulse space-y-6">
|
|
||||||
<div className="h-64 bg-muted rounded-lg"></div>
|
|
||||||
<div className="h-8 bg-muted rounded w-1/2"></div>
|
|
||||||
<div className="h-4 bg-muted rounded w-1/3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -217,16 +212,8 @@ export default function RideDetail() {
|
|||||||
className="mb-4"
|
className="mb-4"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Back Button and Edit Button */}
|
{/* Edit Button */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex justify-end mb-6">
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => navigate(`/parks/${ride.park?.slug}`)}
|
|
||||||
>
|
|
||||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
||||||
Back to {ride.park?.name}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this ride")}
|
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this ride")}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { useState, useEffect, useCallback, lazy, Suspense } from 'react';
|
import { useState, useEffect, useCallback, lazy, Suspense } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { Header } from '@/components/layout/Header';
|
import { Header } from '@/components/layout/Header';
|
||||||
|
import { EntityBreadcrumb } from '@/components/navigation/EntityBreadcrumb';
|
||||||
|
import { CompanyDetailSkeleton } from '@/components/loading/CompanyDetailSkeleton';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
@@ -167,17 +169,7 @@ export default function RideModelDetail() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="container mx-auto px-4 py-8">
|
<CompanyDetailSkeleton />
|
||||||
<div className="animate-pulse space-y-6">
|
|
||||||
<div className="h-12 bg-muted rounded w-1/3"></div>
|
|
||||||
<div className="h-64 bg-muted rounded"></div>
|
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
{[...Array(6)].map((_, i) => (
|
|
||||||
<div key={i} className="h-48 bg-muted rounded-lg"></div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -204,12 +196,25 @@ export default function RideModelDetail() {
|
|||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
<main className="container mx-auto px-4 py-8">
|
<main className="container mx-auto px-4 py-8">
|
||||||
<div className="flex items-center justify-between mb-6">
|
{/* Breadcrumb Navigation */}
|
||||||
<Button variant="ghost" onClick={() => navigate(`/manufacturers/${manufacturerSlug}/models`)}>
|
<EntityBreadcrumb
|
||||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
segments={[
|
||||||
Back to {manufacturer.name} Models
|
{ label: 'Manufacturers', href: '/manufacturers' },
|
||||||
</Button>
|
{
|
||||||
|
label: manufacturer.name,
|
||||||
|
href: `/manufacturers/${manufacturerSlug}`,
|
||||||
|
showPreview: true,
|
||||||
|
previewType: 'company',
|
||||||
|
previewSlug: manufacturerSlug || ''
|
||||||
|
},
|
||||||
|
{ label: 'Models', href: `/manufacturers/${manufacturerSlug}/models` },
|
||||||
|
{ label: model.name }
|
||||||
|
]}
|
||||||
|
className="mb-4"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Edit Button */}
|
||||||
|
<div className="flex justify-end mb-6">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this ride model")}
|
onClick={() => requireAuth(() => setIsEditModalOpen(true), "Sign in to edit this ride model")}
|
||||||
|
|||||||
Reference in New Issue
Block a user