mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 07:31:12 -05:00
Refactor RideForm image handling
This commit is contained in:
@@ -8,7 +8,7 @@ import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { PhotoUpload } from '@/components/upload/PhotoUpload';
|
||||
import { UppyPhotoUpload } from '@/components/upload/UppyPhotoUpload';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { DatePicker } from '@/components/ui/date-picker';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
@@ -65,9 +65,21 @@ const rideSchema = z.object({
|
||||
type RideFormData = z.infer<typeof rideSchema>;
|
||||
|
||||
interface RideFormProps {
|
||||
onSubmit: (data: RideFormData & { image_url?: string }) => Promise<void>;
|
||||
onSubmit: (data: RideFormData & {
|
||||
image_url?: string;
|
||||
banner_image_url?: string;
|
||||
banner_image_id?: string;
|
||||
card_image_url?: string;
|
||||
card_image_id?: string;
|
||||
}) => Promise<void>;
|
||||
onCancel?: () => void;
|
||||
initialData?: Partial<RideFormData & { image_url?: string }>;
|
||||
initialData?: Partial<RideFormData & {
|
||||
image_url?: string;
|
||||
banner_image_url?: string;
|
||||
banner_image_id?: string;
|
||||
card_image_url?: string;
|
||||
card_image_id?: string;
|
||||
}>;
|
||||
isEditing?: boolean;
|
||||
}
|
||||
|
||||
@@ -116,7 +128,10 @@ const intensityLevels = [
|
||||
|
||||
export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: RideFormProps) {
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [rideImage, setRideImage] = useState<string>(initialData?.image_url || '');
|
||||
const [bannerImageUrl, setBannerImageUrl] = useState<string>(initialData?.banner_image_url || '');
|
||||
const [bannerImageId, setBannerImageId] = useState<string>(initialData?.banner_image_id || '');
|
||||
const [cardImageUrl, setCardImageUrl] = useState<string>(initialData?.card_image_url || '');
|
||||
const [cardImageId, setCardImageId] = useState<string>(initialData?.card_image_id || '');
|
||||
const { preferences } = useUnitPreferences();
|
||||
const measurementSystem = preferences.measurement_system;
|
||||
|
||||
@@ -221,7 +236,10 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
drop_height_meters: data.drop_height_meters
|
||||
? convertDistanceToMetric(data.drop_height_meters, measurementSystem)
|
||||
: undefined,
|
||||
image_url: rideImage || undefined
|
||||
banner_image_url: bannerImageUrl || undefined,
|
||||
banner_image_id: bannerImageId || undefined,
|
||||
card_image_url: cardImageUrl || undefined,
|
||||
card_image_id: cardImageId || undefined
|
||||
};
|
||||
|
||||
// Build composite submission if new entities were created
|
||||
@@ -715,25 +733,63 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Image */}
|
||||
<div className="space-y-2">
|
||||
<Label>Ride Image</Label>
|
||||
<PhotoUpload
|
||||
maxFiles={1}
|
||||
variant="default"
|
||||
existingPhotos={rideImage ? [rideImage] : []}
|
||||
onUploadComplete={(urls) => setRideImage(urls[0] || '')}
|
||||
onError={(error) => {
|
||||
toast({
|
||||
title: "Upload Error",
|
||||
description: error,
|
||||
variant: "destructive"
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
High-quality image of the ride (recommended: 800x600px)
|
||||
</p>
|
||||
{/* Images */}
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-semibold">Images</h3>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Banner Image</Label>
|
||||
<UppyPhotoUpload
|
||||
maxFiles={1}
|
||||
variant="public"
|
||||
onUploadComplete={(urls) => {
|
||||
if (urls[0]) {
|
||||
setBannerImageUrl(urls[0]);
|
||||
// Extract image ID from Cloudflare URL (format: https://imagedelivery.net/<hash>/<id>/<variant>)
|
||||
const idMatch = urls[0].match(/\/([^/]+)\/public$/);
|
||||
if (idMatch) {
|
||||
setBannerImageId(idMatch[1]);
|
||||
}
|
||||
}
|
||||
}}
|
||||
showPreview={true}
|
||||
/>
|
||||
{bannerImageUrl && (
|
||||
<div className="mt-2">
|
||||
<img src={bannerImageUrl} alt="Banner preview" className="w-full h-32 object-cover rounded" />
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground">
|
||||
High-resolution banner image for the ride detail page (recommended: 1200x400px)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Card Image</Label>
|
||||
<UppyPhotoUpload
|
||||
maxFiles={1}
|
||||
variant="public"
|
||||
onUploadComplete={(urls) => {
|
||||
if (urls[0]) {
|
||||
setCardImageUrl(urls[0]);
|
||||
// Extract image ID from Cloudflare URL
|
||||
const idMatch = urls[0].match(/\/([^/]+)\/public$/);
|
||||
if (idMatch) {
|
||||
setCardImageId(idMatch[1]);
|
||||
}
|
||||
}
|
||||
}}
|
||||
showPreview={true}
|
||||
/>
|
||||
{cardImageUrl && (
|
||||
<div className="mt-2">
|
||||
<img src={cardImageUrl} alt="Card preview" className="w-full h-32 object-cover rounded" />
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Square or rectangular image for ride cards and listings (recommended: 400x300px)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Form Actions */}
|
||||
|
||||
Reference in New Issue
Block a user