mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 10:11:13 -05:00
Refactor: Implement Cloudflare Image Variants
This commit is contained in:
@@ -3,6 +3,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Star, MapPin, Ruler, FerrisWheel } from 'lucide-react';
|
||||
import { Company } from '@/types/database';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
||||
|
||||
interface DesignerCardProps {
|
||||
company: Company;
|
||||
@@ -38,11 +39,12 @@ export function DesignerCard({ company }: DesignerCardProps) {
|
||||
|
||||
{/* Logo or Icon */}
|
||||
<div className="relative z-10 flex items-center justify-center">
|
||||
{company.logo_url ? (
|
||||
{(company.logo_url || (company as any).logo_image_id) ? (
|
||||
<img
|
||||
src={company.logo_url}
|
||||
src={company.logo_url || getCloudflareImageUrl((company as any).logo_image_id, 'logo')}
|
||||
alt={`${company.name} logo`}
|
||||
className="max-w-20 max-h-20 object-contain filter drop-shadow-sm"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<div className="p-4 rounded-full bg-muted/30 backdrop-blur-sm border border-border/30">
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Company } from '@/types/database';
|
||||
import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
||||
|
||||
interface ManufacturerCardProps {
|
||||
company: Company;
|
||||
@@ -42,9 +43,15 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
|
||||
<div className="aspect-video relative bg-gradient-to-br from-primary/20 via-primary/10 to-transparent overflow-hidden">
|
||||
{(company.card_image_url || company.card_image_id) ? (
|
||||
<img
|
||||
src={company.card_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${company.card_image_id}/public`}
|
||||
src={company.card_image_url || getCloudflareImageUrl(company.card_image_id, 'card')}
|
||||
srcSet={company.card_image_id ? `
|
||||
${getCloudflareImageUrl(company.card_image_id, 'cardthumb')} 600w,
|
||||
${getCloudflareImageUrl(company.card_image_id, 'card')} 1200w
|
||||
` : undefined}
|
||||
sizes="(max-width: 640px) 600px, 1200px"
|
||||
alt={company.name}
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
@@ -59,12 +66,13 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
|
||||
|
||||
{/* Logo Display */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
{company.logo_url ? (
|
||||
{(company.logo_url || (company as any).logo_image_id) ? (
|
||||
<div className="w-16 h-16 md:w-20 md:h-20 bg-background/90 rounded-xl overflow-hidden shadow-lg backdrop-blur-sm border border-border/50">
|
||||
<img
|
||||
src={company.logo_url}
|
||||
src={company.logo_url || getCloudflareImageUrl((company as any).logo_image_id, 'logo')}
|
||||
alt={`${company.name} logo`}
|
||||
className="w-full h-full object-contain p-2"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Building, Star, MapPin } from 'lucide-react';
|
||||
import { Company } from '@/types/database';
|
||||
import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
||||
|
||||
interface OperatorCardProps {
|
||||
company: Company;
|
||||
@@ -29,9 +30,15 @@ const OperatorCard = ({ company }: OperatorCardProps) => {
|
||||
<div className="aspect-video relative bg-gradient-to-br from-primary/20 via-primary/10 to-transparent overflow-hidden">
|
||||
{(company.card_image_url || company.card_image_id) ? (
|
||||
<img
|
||||
src={company.card_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${company.card_image_id}/public`}
|
||||
src={company.card_image_url || getCloudflareImageUrl(company.card_image_id, 'card')}
|
||||
srcSet={company.card_image_id ? `
|
||||
${getCloudflareImageUrl(company.card_image_id, 'cardthumb')} 600w,
|
||||
${getCloudflareImageUrl(company.card_image_id, 'card')} 1200w
|
||||
` : undefined}
|
||||
sizes="(max-width: 640px) 600px, 1200px"
|
||||
alt={company.name}
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
@@ -46,12 +53,13 @@ const OperatorCard = ({ company }: OperatorCardProps) => {
|
||||
|
||||
{/* Logo Display */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
{company.logo_url ? (
|
||||
{(company.logo_url || (company as any).logo_image_id) ? (
|
||||
<div className="w-20 h-20 bg-background/90 rounded-xl overflow-hidden shadow-lg backdrop-blur-sm border border-border/50">
|
||||
<img
|
||||
src={company.logo_url}
|
||||
src={company.logo_url || getCloudflareImageUrl((company as any).logo_image_id, 'logo')}
|
||||
alt={`${company.name} logo`}
|
||||
className="w-full h-full object-contain p-2"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Building2, Star, MapPin } from 'lucide-react';
|
||||
import { Company } from '@/types/database';
|
||||
import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
||||
|
||||
interface ParkOwnerCardProps {
|
||||
company: Company;
|
||||
@@ -29,9 +30,15 @@ const ParkOwnerCard = ({ company }: ParkOwnerCardProps) => {
|
||||
<div className="aspect-video relative bg-gradient-to-br from-primary/20 via-primary/10 to-transparent overflow-hidden">
|
||||
{(company.card_image_url || company.card_image_id) ? (
|
||||
<img
|
||||
src={company.card_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${company.card_image_id}/public`}
|
||||
src={company.card_image_url || getCloudflareImageUrl(company.card_image_id, 'card')}
|
||||
srcSet={company.card_image_id ? `
|
||||
${getCloudflareImageUrl(company.card_image_id, 'cardthumb')} 600w,
|
||||
${getCloudflareImageUrl(company.card_image_id, 'card')} 1200w
|
||||
` : undefined}
|
||||
sizes="(max-width: 640px) 600px, 1200px"
|
||||
alt={company.name}
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
@@ -46,12 +53,13 @@ const ParkOwnerCard = ({ company }: ParkOwnerCardProps) => {
|
||||
|
||||
{/* Logo Display */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
{company.logo_url ? (
|
||||
{(company.logo_url || (company as any).logo_image_id) ? (
|
||||
<div className="w-20 h-20 bg-background/90 rounded-xl overflow-hidden shadow-lg backdrop-blur-sm border border-border/50">
|
||||
<img
|
||||
src={company.logo_url}
|
||||
src={company.logo_url || getCloudflareImageUrl((company as any).logo_image_id, 'logo')}
|
||||
alt={`${company.name} logo`}
|
||||
className="w-full h-full object-contain p-2"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Park } from '@/types/database';
|
||||
import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
||||
|
||||
interface ParkCardProps {
|
||||
park: Park;
|
||||
@@ -48,9 +49,15 @@ export function ParkCard({ park }: ParkCardProps) {
|
||||
<div className="aspect-[4/3] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 flex items-center justify-center relative">
|
||||
{(park.card_image_url || park.card_image_id) ? (
|
||||
<img
|
||||
src={park.card_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${park.card_image_id}/public`}
|
||||
src={park.card_image_url || getCloudflareImageUrl(park.card_image_id, 'card')}
|
||||
srcSet={park.card_image_id ? `
|
||||
${getCloudflareImageUrl(park.card_image_id, 'cardthumb')} 600w,
|
||||
${getCloudflareImageUrl(park.card_image_id, 'card')} 1200w
|
||||
` : undefined}
|
||||
sizes="(max-width: 640px) 600px, 1200px"
|
||||
alt={park.name}
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<div className="opacity-50 flex items-center justify-center">
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Star, MapPin, Clock, Zap, FerrisWheel, Waves, Theater, Train } from 'lucide-react';
|
||||
import { MeasurementDisplay } from '@/components/ui/measurement-display';
|
||||
import { Ride } from '@/types/database';
|
||||
import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
||||
|
||||
interface RideCardProps {
|
||||
ride: Ride;
|
||||
@@ -57,9 +58,15 @@ export function RideCard({ ride, showParkName = true, className, parkSlug }: Rid
|
||||
<div className="aspect-[4/3] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 flex items-center justify-center relative">
|
||||
{(ride.card_image_url || ride.card_image_id || ride.image_url) ? (
|
||||
<img
|
||||
src={ride.card_image_url || (ride.card_image_id ? `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${ride.card_image_id}/public` : ride.image_url)}
|
||||
src={ride.card_image_url || getCloudflareImageUrl(ride.card_image_id, 'card') || ride.image_url}
|
||||
srcSet={ride.card_image_id ? `
|
||||
${getCloudflareImageUrl(ride.card_image_id, 'cardthumb')} 600w,
|
||||
${getCloudflareImageUrl(ride.card_image_id, 'card')} 1200w
|
||||
` : undefined}
|
||||
sizes="(max-width: 640px) 600px, 1200px"
|
||||
alt={ride.name}
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<div className="opacity-50 flex items-center justify-center">
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { FerrisWheel } from 'lucide-react';
|
||||
import { RideModel } from '@/types/database';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
||||
|
||||
interface RideModelCardProps {
|
||||
model: RideModel;
|
||||
@@ -45,9 +46,15 @@ export function RideModelCard({ model, manufacturerSlug }: RideModelCardProps) {
|
||||
>
|
||||
{(cardImageUrl || cardImageId) ? (
|
||||
<img
|
||||
src={cardImageUrl || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${cardImageId}/public`}
|
||||
src={cardImageUrl || getCloudflareImageUrl(cardImageId, 'card')}
|
||||
srcSet={cardImageId ? `
|
||||
${getCloudflareImageUrl(cardImageId, 'cardthumb')} 600w,
|
||||
${getCloudflareImageUrl(cardImageId, 'card')} 1200w
|
||||
` : undefined}
|
||||
sizes="(max-width: 640px) 600px, 1200px"
|
||||
alt={model.name}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Image as ImageIcon, ImagePlus, X } from 'lucide-react';
|
||||
import { UppyPhotoUpload } from './UppyPhotoUpload';
|
||||
import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
||||
|
||||
export type ImageType = 'logo' | 'banner' | 'card';
|
||||
|
||||
@@ -140,7 +141,15 @@ export function EntityImageUploader({
|
||||
{hasImage ? (
|
||||
<div className="relative aspect-[16/9] bg-muted">
|
||||
<img
|
||||
src={currentImage.url}
|
||||
src={
|
||||
currentImage.url || (
|
||||
type === 'logo'
|
||||
? getCloudflareImageUrl(currentImage.id, 'logo')
|
||||
: type === 'banner'
|
||||
? getCloudflareImageUrl(currentImage.id, 'banner')
|
||||
: getCloudflareImageUrl(currentImage.id, 'card')
|
||||
)
|
||||
}
|
||||
alt={`${spec.label} preview`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
@@ -142,7 +142,8 @@ export function UppyPhotoUpload({
|
||||
if (statusResponse.ok) {
|
||||
const status: UploadSuccessResponse = await statusResponse.json();
|
||||
if (status.uploaded && status.urls) {
|
||||
return status.urls.public;
|
||||
const CLOUDFLARE_ACCOUNT_HASH = import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH;
|
||||
return `https://imagedelivery.net/${CLOUDFLARE_ACCOUNT_HASH}/${cloudflareId}/public`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
61
src/lib/cloudflareImageUtils.ts
Normal file
61
src/lib/cloudflareImageUtils.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Cloudflare Images variant utilities
|
||||
* Generates properly formatted URLs for Cloudflare Image variants
|
||||
*/
|
||||
|
||||
export type CloudflareVariant =
|
||||
| 'avatar'
|
||||
| 'banner'
|
||||
| 'bannermobile'
|
||||
| 'card'
|
||||
| 'cardthumb'
|
||||
| 'logo'
|
||||
| 'public';
|
||||
|
||||
const CLOUDFLARE_ACCOUNT_HASH = import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH;
|
||||
|
||||
/**
|
||||
* Build a Cloudflare image URL with specified variant
|
||||
*/
|
||||
export function getCloudflareImageUrl(
|
||||
imageId: string | undefined,
|
||||
variant: CloudflareVariant = 'public'
|
||||
): string | undefined {
|
||||
if (!imageId) return undefined;
|
||||
return `https://imagedelivery.net/${CLOUDFLARE_ACCOUNT_HASH}/${imageId}/${variant}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate responsive image srcset for card images
|
||||
* Useful for <img srcset> elements
|
||||
*/
|
||||
export function getCloudflareImageSrcSet(imageId: string | undefined): string | undefined {
|
||||
if (!imageId) return undefined;
|
||||
|
||||
return [
|
||||
`${getCloudflareImageUrl(imageId, 'cardthumb')} 600w`,
|
||||
`${getCloudflareImageUrl(imageId, 'card')} 1200w`,
|
||||
`${getCloudflareImageUrl(imageId, 'public')} 1366w`
|
||||
].join(', ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get responsive banner URLs for mobile and desktop
|
||||
*/
|
||||
export function getBannerUrls(imageId: string | undefined) {
|
||||
if (!imageId) return { mobile: undefined, desktop: undefined };
|
||||
|
||||
return {
|
||||
mobile: getCloudflareImageUrl(imageId, 'bannermobile'),
|
||||
desktop: getCloudflareImageUrl(imageId, 'banner')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract Cloudflare image ID from various URL formats
|
||||
*/
|
||||
export function extractCloudflareImageId(url: string): string | null {
|
||||
// Match imagedelivery.net URLs
|
||||
const match = url.match(/imagedelivery\.net\/[^\/]+\/([a-f0-9-]+)\//i);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
@@ -60,9 +60,11 @@ export async function uploadPendingImages(images: UploadedImage[]): Promise<Uplo
|
||||
// Clean up object URL
|
||||
URL.revokeObjectURL(image.url);
|
||||
|
||||
const CLOUDFLARE_ACCOUNT_HASH = import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH;
|
||||
|
||||
// Step 3: Return uploaded image metadata with wasNewlyUploaded flag
|
||||
return {
|
||||
url: result.result.variants[0], // Use first variant (usually the original)
|
||||
url: `https://imagedelivery.net/${CLOUDFLARE_ACCOUNT_HASH}/${result.result.id}/public`,
|
||||
cloudflare_id: result.result.id,
|
||||
caption: image.caption,
|
||||
isLocal: false,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
@@ -169,11 +170,18 @@ export default function DesignerDetail() {
|
||||
<div className="relative mb-8">
|
||||
<div className="aspect-[21/9] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 rounded-lg overflow-hidden relative">
|
||||
{(designer.banner_image_url || designer.banner_image_id) ? (
|
||||
<img
|
||||
src={designer.banner_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${designer.banner_image_id}/public`}
|
||||
alt={designer.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<picture>
|
||||
<source
|
||||
media="(max-width: 768px)"
|
||||
srcSet={getBannerUrls(designer.banner_image_id).mobile || designer.banner_image_url}
|
||||
/>
|
||||
<img
|
||||
src={getBannerUrls(designer.banner_image_id).desktop || designer.banner_image_url}
|
||||
alt={designer.name}
|
||||
className="w-full h-full object-cover"
|
||||
loading="eager"
|
||||
/>
|
||||
</picture>
|
||||
) : designer.logo_url ? (
|
||||
<div className="flex items-center justify-center h-full bg-background/90">
|
||||
<img
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
@@ -181,11 +182,18 @@ export default function ManufacturerDetail() {
|
||||
<div className="relative mb-4 md:mb-8">
|
||||
<div className="aspect-[16/9] md:aspect-[21/9] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 rounded-lg overflow-hidden relative">
|
||||
{(manufacturer.banner_image_url || manufacturer.banner_image_id) ? (
|
||||
<img
|
||||
src={manufacturer.banner_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${manufacturer.banner_image_id}/public`}
|
||||
alt={manufacturer.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<picture>
|
||||
<source
|
||||
media="(max-width: 768px)"
|
||||
srcSet={getBannerUrls(manufacturer.banner_image_id).mobile || manufacturer.banner_image_url}
|
||||
/>
|
||||
<img
|
||||
src={getBannerUrls(manufacturer.banner_image_id).desktop || manufacturer.banner_image_url}
|
||||
alt={manufacturer.name}
|
||||
className="w-full h-full object-cover"
|
||||
loading="eager"
|
||||
/>
|
||||
</picture>
|
||||
) : manufacturer.logo_url ? (
|
||||
<div className="flex items-center justify-center h-full bg-background/90">
|
||||
<img
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
@@ -214,11 +215,18 @@ export default function OperatorDetail() {
|
||||
<div className="relative mb-8">
|
||||
<div className="aspect-[21/9] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 rounded-lg overflow-hidden relative">
|
||||
{(operator.banner_image_url || operator.banner_image_id) ? (
|
||||
<img
|
||||
src={operator.banner_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${operator.banner_image_id}/public`}
|
||||
alt={operator.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<picture>
|
||||
<source
|
||||
media="(max-width: 768px)"
|
||||
srcSet={getBannerUrls(operator.banner_image_id).mobile || operator.banner_image_url}
|
||||
/>
|
||||
<img
|
||||
src={getBannerUrls(operator.banner_image_id).desktop || operator.banner_image_url}
|
||||
alt={operator.name}
|
||||
className="w-full h-full object-cover"
|
||||
loading="eager"
|
||||
/>
|
||||
</picture>
|
||||
) : operator.logo_url ? (
|
||||
<div className="flex items-center justify-center h-full bg-background/90">
|
||||
<img
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@@ -244,11 +245,18 @@ export default function ParkDetail() {
|
||||
<div className="relative mb-8">
|
||||
<div className="aspect-[21/9] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 rounded-lg overflow-hidden relative">
|
||||
{(park.banner_image_url || park.banner_image_id) ? (
|
||||
<img
|
||||
src={park.banner_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${park.banner_image_id}/public`}
|
||||
alt={park.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<picture>
|
||||
<source
|
||||
media="(max-width: 768px)"
|
||||
srcSet={getBannerUrls(park.banner_image_id).mobile || park.banner_image_url}
|
||||
/>
|
||||
<img
|
||||
src={getBannerUrls(park.banner_image_id).desktop || park.banner_image_url}
|
||||
alt={park.name}
|
||||
className="w-full h-full object-cover"
|
||||
loading="eager"
|
||||
/>
|
||||
</picture>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="opacity-50">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
@@ -214,11 +215,18 @@ export default function PropertyOwnerDetail() {
|
||||
<div className="relative mb-8">
|
||||
<div className="aspect-[21/9] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 rounded-lg overflow-hidden relative">
|
||||
{(owner.banner_image_url || owner.banner_image_id) ? (
|
||||
<img
|
||||
src={owner.banner_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${owner.banner_image_id}/public`}
|
||||
alt={owner.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<picture>
|
||||
<source
|
||||
media="(max-width: 768px)"
|
||||
srcSet={getBannerUrls(owner.banner_image_id).mobile || owner.banner_image_url}
|
||||
/>
|
||||
<img
|
||||
src={getBannerUrls(owner.banner_image_id).desktop || owner.banner_image_url}
|
||||
alt={owner.name}
|
||||
className="w-full h-full object-cover"
|
||||
loading="eager"
|
||||
/>
|
||||
</picture>
|
||||
) : owner.logo_url ? (
|
||||
<div className="flex items-center justify-center h-full bg-background/90">
|
||||
<img
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import { getBannerUrls } from '@/lib/cloudflareImageUtils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@@ -241,11 +242,18 @@ export default function RideDetail() {
|
||||
<div className="relative mb-8">
|
||||
<div className="aspect-[21/9] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 rounded-lg overflow-hidden relative">
|
||||
{(ride.banner_image_url || ride.banner_image_id) ? (
|
||||
<img
|
||||
src={ride.banner_image_url || `https://imagedelivery.net/${import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH}/${ride.banner_image_id}/public`}
|
||||
alt={ride.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<picture>
|
||||
<source
|
||||
media="(max-width: 768px)"
|
||||
srcSet={getBannerUrls(ride.banner_image_id).mobile || ride.banner_image_url}
|
||||
/>
|
||||
<img
|
||||
src={getBannerUrls(ride.banner_image_id).desktop || ride.banner_image_url}
|
||||
alt={ride.name}
|
||||
className="w-full h-full object-cover"
|
||||
loading="eager"
|
||||
/>
|
||||
</picture>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="opacity-50">
|
||||
|
||||
Reference in New Issue
Block a user