Fix: Resolve image upload and form integration issues

This commit is contained in:
gpt-engineer-app[bot]
2025-10-01 19:02:21 +00:00
parent 37b70111c6
commit 83260e7f73
6 changed files with 57 additions and 199 deletions

View File

@@ -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 { UppyPhotoUpload } from '@/components/upload/UppyPhotoUpload';
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
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';
@@ -61,27 +61,25 @@ const rideSchema = z.object({
technical_specs: z.string().optional(),
// Manufacturer and model
manufacturer_id: z.string().uuid().optional(),
ride_model_id: z.string().uuid().optional()
ride_model_id: z.string().uuid().optional(),
// Images
images: z.object({
uploaded: z.array(z.object({
url: z.string(),
cloudflare_id: z.string(),
caption: z.string().optional(),
})),
banner_assignment: z.number().optional(),
card_assignment: z.number().optional(),
}).optional()
});
type RideFormData = z.infer<typeof rideSchema>;
interface RideFormProps {
onSubmit: (data: RideFormData & {
image_url?: string;
banner_image_url?: string;
banner_image_id?: string;
card_image_url?: string;
card_image_id?: string;
}) => Promise<void>;
onSubmit: (data: RideFormData) => Promise<void>;
onCancel?: () => void;
initialData?: Partial<RideFormData & {
image_url?: string;
banner_image_url?: string;
banner_image_id?: string;
card_image_url?: string;
card_image_id?: string;
}>;
initialData?: Partial<RideFormData>;
isEditing?: boolean;
}
@@ -131,10 +129,6 @@ const intensityLevels = [
export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: RideFormProps) {
const { isModerator } = useUserRole();
const [submitting, setSubmitting] = useState(false);
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;
@@ -197,7 +191,8 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
coaster_stats: initialData?.coaster_stats || '',
technical_specs: initialData?.technical_specs || '',
manufacturer_id: initialData?.manufacturer_id || undefined,
ride_model_id: initialData?.ride_model_id || undefined
ride_model_id: initialData?.ride_model_id || undefined,
images: { uploaded: [] }
}
});
@@ -224,11 +219,7 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
: undefined,
drop_height_meters: data.drop_height_meters
? convertDistanceToMetric(data.drop_height_meters, measurementSystem)
: undefined,
banner_image_url: bannerImageUrl || undefined,
banner_image_id: bannerImageId || undefined,
card_image_url: cardImageUrl || undefined,
card_image_id: cardImageId || undefined
: undefined
};
// Build composite submission if new entities were created
@@ -714,63 +705,12 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
</div>
{/* 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>
<EntityMultiImageUploader
mode={isEditing ? 'edit' : 'create'}
value={watch('images') || { uploaded: [] }}
onChange={(images: ImageAssignments) => setValue('images', images)}
entityType="ride"
/>
{/* Form Actions */}
<div className="flex gap-4 pt-6">