mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 06:31:13 -05:00
Refactor: Update image upload components
This commit is contained in:
@@ -12,6 +12,7 @@ import { Ruler, Save, X } from 'lucide-react';
|
||||
import { Combobox } from '@/components/ui/combobox';
|
||||
import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData';
|
||||
import { useUserRole } from '@/hooks/useUserRole';
|
||||
import { EntityImageUploader } from '@/components/upload/EntityImageUploader';
|
||||
|
||||
const designerSchema = z.object({
|
||||
name: z.string().min(1, 'Name is required'),
|
||||
@@ -20,7 +21,12 @@ const designerSchema = z.object({
|
||||
person_type: z.enum(['company', 'individual', 'firm', 'organization']),
|
||||
website_url: z.string().url().optional().or(z.literal('')),
|
||||
founded_year: z.number().min(1800).max(new Date().getFullYear()).optional(),
|
||||
headquarters_location: z.string().optional()
|
||||
headquarters_location: z.string().optional(),
|
||||
logo_url: z.string().optional(),
|
||||
banner_image_id: z.string().optional(),
|
||||
banner_image_url: z.string().optional(),
|
||||
card_image_id: z.string().optional(),
|
||||
card_image_url: z.string().optional()
|
||||
});
|
||||
|
||||
type DesignerFormData = z.infer<typeof designerSchema>;
|
||||
@@ -50,7 +56,12 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr
|
||||
person_type: initialData?.person_type || 'company',
|
||||
website_url: initialData?.website_url || '',
|
||||
founded_year: initialData?.founded_year || undefined,
|
||||
headquarters_location: initialData?.headquarters_location || ''
|
||||
headquarters_location: initialData?.headquarters_location || '',
|
||||
logo_url: initialData?.logo_url || '',
|
||||
banner_image_id: initialData?.banner_image_id || '',
|
||||
banner_image_url: initialData?.banner_image_url || '',
|
||||
card_image_id: initialData?.card_image_id || '',
|
||||
card_image_url: initialData?.card_image_url || ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -169,6 +180,24 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Images */}
|
||||
<EntityImageUploader
|
||||
images={{
|
||||
logo: { url: watch('logo_url') },
|
||||
banner: { url: watch('banner_image_url'), id: watch('banner_image_id') },
|
||||
card: { url: watch('card_image_url'), id: watch('card_image_id') }
|
||||
}}
|
||||
onImagesChange={(images) => {
|
||||
if (images.logo_url !== undefined) setValue('logo_url', images.logo_url);
|
||||
if (images.banner_image_id !== undefined) setValue('banner_image_id', images.banner_image_id);
|
||||
if (images.banner_image_url !== undefined) setValue('banner_image_url', images.banner_image_url);
|
||||
if (images.card_image_id !== undefined) setValue('card_image_id', images.card_image_id);
|
||||
if (images.card_image_url !== undefined) setValue('card_image_url', images.card_image_url);
|
||||
}}
|
||||
showLogo={true}
|
||||
entityType="designer"
|
||||
/>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-3 justify-end">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Building2, Save, X } from 'lucide-react';
|
||||
import { Combobox } from '@/components/ui/combobox';
|
||||
import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData';
|
||||
import { useUserRole } from '@/hooks/useUserRole';
|
||||
import { EntityImageUploader } from '@/components/upload/EntityImageUploader';
|
||||
|
||||
const manufacturerSchema = z.object({
|
||||
name: z.string().min(1, 'Name is required'),
|
||||
@@ -20,7 +21,12 @@ const manufacturerSchema = z.object({
|
||||
person_type: z.enum(['company', 'individual', 'firm', 'organization']),
|
||||
website_url: z.string().url().optional().or(z.literal('')),
|
||||
founded_year: z.number().min(1800).max(new Date().getFullYear()).optional(),
|
||||
headquarters_location: z.string().optional()
|
||||
headquarters_location: z.string().optional(),
|
||||
logo_url: z.string().optional(),
|
||||
banner_image_id: z.string().optional(),
|
||||
banner_image_url: z.string().optional(),
|
||||
card_image_id: z.string().optional(),
|
||||
card_image_url: z.string().optional()
|
||||
});
|
||||
|
||||
type ManufacturerFormData = z.infer<typeof manufacturerSchema>;
|
||||
@@ -50,7 +56,12 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur
|
||||
person_type: initialData?.person_type || 'company',
|
||||
website_url: initialData?.website_url || '',
|
||||
founded_year: initialData?.founded_year || undefined,
|
||||
headquarters_location: initialData?.headquarters_location || ''
|
||||
headquarters_location: initialData?.headquarters_location || '',
|
||||
logo_url: initialData?.logo_url || '',
|
||||
banner_image_id: initialData?.banner_image_id || '',
|
||||
banner_image_url: initialData?.banner_image_url || '',
|
||||
card_image_id: initialData?.card_image_id || '',
|
||||
card_image_url: initialData?.card_image_url || ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -169,6 +180,24 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Images */}
|
||||
<EntityImageUploader
|
||||
images={{
|
||||
logo: { url: watch('logo_url') },
|
||||
banner: { url: watch('banner_image_url'), id: watch('banner_image_id') },
|
||||
card: { url: watch('card_image_url'), id: watch('card_image_id') }
|
||||
}}
|
||||
onImagesChange={(images) => {
|
||||
if (images.logo_url !== undefined) setValue('logo_url', images.logo_url);
|
||||
if (images.banner_image_id !== undefined) setValue('banner_image_id', images.banner_image_id);
|
||||
if (images.banner_image_url !== undefined) setValue('banner_image_url', images.banner_image_url);
|
||||
if (images.card_image_id !== undefined) setValue('card_image_id', images.card_image_id);
|
||||
if (images.card_image_url !== undefined) setValue('card_image_url', images.card_image_url);
|
||||
}}
|
||||
showLogo={true}
|
||||
entityType="manufacturer"
|
||||
/>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-3 justify-end">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { FerrisWheel, Save, X } from 'lucide-react';
|
||||
import { Combobox } from '@/components/ui/combobox';
|
||||
import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData';
|
||||
import { useUserRole } from '@/hooks/useUserRole';
|
||||
import { EntityImageUploader } from '@/components/upload/EntityImageUploader';
|
||||
|
||||
const operatorSchema = z.object({
|
||||
name: z.string().min(1, 'Name is required'),
|
||||
@@ -20,7 +21,12 @@ const operatorSchema = z.object({
|
||||
person_type: z.enum(['company', 'individual', 'firm', 'organization']),
|
||||
website_url: z.string().url().optional().or(z.literal('')),
|
||||
founded_year: z.number().min(1800).max(new Date().getFullYear()).optional(),
|
||||
headquarters_location: z.string().optional()
|
||||
headquarters_location: z.string().optional(),
|
||||
logo_url: z.string().optional(),
|
||||
banner_image_id: z.string().optional(),
|
||||
banner_image_url: z.string().optional(),
|
||||
card_image_id: z.string().optional(),
|
||||
card_image_url: z.string().optional()
|
||||
});
|
||||
|
||||
type OperatorFormData = z.infer<typeof operatorSchema>;
|
||||
@@ -50,7 +56,12 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr
|
||||
person_type: initialData?.person_type || 'company',
|
||||
website_url: initialData?.website_url || '',
|
||||
founded_year: initialData?.founded_year || undefined,
|
||||
headquarters_location: initialData?.headquarters_location || ''
|
||||
headquarters_location: initialData?.headquarters_location || '',
|
||||
logo_url: initialData?.logo_url || '',
|
||||
banner_image_id: initialData?.banner_image_id || '',
|
||||
banner_image_url: initialData?.banner_image_url || '',
|
||||
card_image_id: initialData?.card_image_id || '',
|
||||
card_image_url: initialData?.card_image_url || ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -169,6 +180,24 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Images */}
|
||||
<EntityImageUploader
|
||||
images={{
|
||||
logo: { url: watch('logo_url') },
|
||||
banner: { url: watch('banner_image_url'), id: watch('banner_image_id') },
|
||||
card: { url: watch('card_image_url'), id: watch('card_image_id') }
|
||||
}}
|
||||
onImagesChange={(images) => {
|
||||
if (images.logo_url !== undefined) setValue('logo_url', images.logo_url);
|
||||
if (images.banner_image_id !== undefined) setValue('banner_image_id', images.banner_image_id);
|
||||
if (images.banner_image_url !== undefined) setValue('banner_image_url', images.banner_image_url);
|
||||
if (images.card_image_id !== undefined) setValue('card_image_id', images.card_image_id);
|
||||
if (images.card_image_url !== undefined) setValue('card_image_url', images.card_image_url);
|
||||
}}
|
||||
showLogo={true}
|
||||
entityType="operator"
|
||||
/>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-3 justify-end">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Building2, Save, X } from 'lucide-react';
|
||||
import { Combobox } from '@/components/ui/combobox';
|
||||
import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData';
|
||||
import { useUserRole } from '@/hooks/useUserRole';
|
||||
import { EntityImageUploader } from '@/components/upload/EntityImageUploader';
|
||||
|
||||
const propertyOwnerSchema = z.object({
|
||||
name: z.string().min(1, 'Name is required'),
|
||||
@@ -20,7 +21,12 @@ const propertyOwnerSchema = z.object({
|
||||
person_type: z.enum(['company', 'individual', 'firm', 'organization']),
|
||||
website_url: z.string().url().optional().or(z.literal('')),
|
||||
founded_year: z.number().min(1800).max(new Date().getFullYear()).optional(),
|
||||
headquarters_location: z.string().optional()
|
||||
headquarters_location: z.string().optional(),
|
||||
logo_url: z.string().optional(),
|
||||
banner_image_id: z.string().optional(),
|
||||
banner_image_url: z.string().optional(),
|
||||
card_image_id: z.string().optional(),
|
||||
card_image_url: z.string().optional()
|
||||
});
|
||||
|
||||
type PropertyOwnerFormData = z.infer<typeof propertyOwnerSchema>;
|
||||
@@ -50,7 +56,12 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO
|
||||
person_type: initialData?.person_type || 'company',
|
||||
website_url: initialData?.website_url || '',
|
||||
founded_year: initialData?.founded_year || undefined,
|
||||
headquarters_location: initialData?.headquarters_location || ''
|
||||
headquarters_location: initialData?.headquarters_location || '',
|
||||
logo_url: initialData?.logo_url || '',
|
||||
banner_image_id: initialData?.banner_image_id || '',
|
||||
banner_image_url: initialData?.banner_image_url || '',
|
||||
card_image_id: initialData?.card_image_id || '',
|
||||
card_image_url: initialData?.card_image_url || ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -169,6 +180,24 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Images */}
|
||||
<EntityImageUploader
|
||||
images={{
|
||||
logo: { url: watch('logo_url') },
|
||||
banner: { url: watch('banner_image_url'), id: watch('banner_image_id') },
|
||||
card: { url: watch('card_image_url'), id: watch('card_image_id') }
|
||||
}}
|
||||
onImagesChange={(images) => {
|
||||
if (images.logo_url !== undefined) setValue('logo_url', images.logo_url);
|
||||
if (images.banner_image_id !== undefined) setValue('banner_image_id', images.banner_image_id);
|
||||
if (images.banner_image_url !== undefined) setValue('banner_image_url', images.banner_image_url);
|
||||
if (images.card_image_id !== undefined) setValue('card_image_id', images.card_image_id);
|
||||
if (images.card_image_url !== undefined) setValue('card_image_url', images.card_image_url);
|
||||
}}
|
||||
showLogo={true}
|
||||
entityType="property_owner"
|
||||
/>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-3 justify-end">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { SlugField } from '@/components/ui/slug-field';
|
||||
import { Layers, Save, X } from 'lucide-react';
|
||||
import { useUserRole } from '@/hooks/useUserRole';
|
||||
import { EntityImageUploader } from '@/components/upload/EntityImageUploader';
|
||||
|
||||
const rideModelSchema = z.object({
|
||||
name: z.string().min(1, 'Name is required'),
|
||||
@@ -18,7 +19,11 @@ const rideModelSchema = z.object({
|
||||
category: z.string().min(1, 'Category is required'),
|
||||
ride_type: z.string().min(1, 'Ride type is required'),
|
||||
description: z.string().optional(),
|
||||
technical_specs: z.string().optional()
|
||||
technical_specs: z.string().optional(),
|
||||
banner_image_id: z.string().optional(),
|
||||
banner_image_url: z.string().optional(),
|
||||
card_image_id: z.string().optional(),
|
||||
card_image_url: z.string().optional()
|
||||
});
|
||||
|
||||
type RideModelFormData = z.infer<typeof rideModelSchema>;
|
||||
@@ -63,7 +68,11 @@ export function RideModelForm({
|
||||
category: initialData?.category || '',
|
||||
ride_type: initialData?.ride_type || '',
|
||||
description: initialData?.description || '',
|
||||
technical_specs: initialData?.technical_specs || ''
|
||||
technical_specs: initialData?.technical_specs || '',
|
||||
banner_image_id: initialData?.banner_image_id || '',
|
||||
banner_image_url: initialData?.banner_image_url || '',
|
||||
card_image_id: initialData?.card_image_id || '',
|
||||
card_image_url: initialData?.card_image_url || ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -166,6 +175,22 @@ export function RideModelForm({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Images */}
|
||||
<EntityImageUploader
|
||||
images={{
|
||||
banner: { url: watch('banner_image_url'), id: watch('banner_image_id') },
|
||||
card: { url: watch('card_image_url'), id: watch('card_image_id') }
|
||||
}}
|
||||
onImagesChange={(images) => {
|
||||
if (images.banner_image_id !== undefined) setValue('banner_image_id', images.banner_image_id);
|
||||
if (images.banner_image_url !== undefined) setValue('banner_image_url', images.banner_image_url);
|
||||
if (images.card_image_id !== undefined) setValue('card_image_id', images.card_image_id);
|
||||
if (images.card_image_url !== undefined) setValue('card_image_url', images.card_image_url);
|
||||
}}
|
||||
showLogo={false}
|
||||
entityType="ride_model"
|
||||
/>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-3 justify-end">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
|
||||
202
src/components/upload/EntityImageUploader.tsx
Normal file
202
src/components/upload/EntityImageUploader.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import { useState } from 'react';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Label } from '@/components/ui/label';
|
||||
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';
|
||||
|
||||
export type ImageType = 'logo' | 'banner' | 'card';
|
||||
|
||||
interface ImageSlot {
|
||||
type: ImageType;
|
||||
url?: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
interface EntityImageUploaderProps {
|
||||
images: {
|
||||
logo?: { url?: string; id?: string };
|
||||
banner?: { url?: string; id?: string };
|
||||
card?: { url?: string; id?: string };
|
||||
};
|
||||
onImagesChange: (images: {
|
||||
logo_url?: string;
|
||||
banner_image_id?: string;
|
||||
banner_image_url?: string;
|
||||
card_image_id?: string;
|
||||
card_image_url?: string;
|
||||
}) => void;
|
||||
showLogo?: boolean;
|
||||
entityType?: string;
|
||||
}
|
||||
|
||||
const IMAGE_SPECS = {
|
||||
logo: { label: 'Logo', aspect: '1:1', dimensions: '400x400', description: 'Square logo image' },
|
||||
banner: { label: 'Banner', aspect: '21:9', dimensions: '1920x820', description: 'Wide header image' },
|
||||
card: { label: 'Card', aspect: '16:9', dimensions: '1200x675', description: 'Preview thumbnail' }
|
||||
};
|
||||
|
||||
export function EntityImageUploader({
|
||||
images,
|
||||
onImagesChange,
|
||||
showLogo = true,
|
||||
entityType = 'entity'
|
||||
}: EntityImageUploaderProps) {
|
||||
const [activeSlot, setActiveSlot] = useState<ImageType | null>(null);
|
||||
|
||||
const handleUploadComplete = (type: ImageType, urls: string[]) => {
|
||||
if (urls.length === 0) return;
|
||||
|
||||
const url = urls[0];
|
||||
const id = url.split('/').pop()?.split('?')[0] || '';
|
||||
|
||||
const updates: any = {};
|
||||
|
||||
if (type === 'logo') {
|
||||
updates.logo_url = url;
|
||||
} else if (type === 'banner') {
|
||||
updates.banner_image_id = id;
|
||||
updates.banner_image_url = url;
|
||||
} else if (type === 'card') {
|
||||
updates.card_image_id = id;
|
||||
updates.card_image_url = url;
|
||||
}
|
||||
|
||||
onImagesChange({
|
||||
logo_url: type === 'logo' ? url : images.logo?.url,
|
||||
banner_image_id: type === 'banner' ? id : images.banner?.id,
|
||||
banner_image_url: type === 'banner' ? url : images.banner?.url,
|
||||
card_image_id: type === 'card' ? id : images.card?.id,
|
||||
card_image_url: type === 'card' ? url : images.card?.url
|
||||
});
|
||||
|
||||
setActiveSlot(null);
|
||||
};
|
||||
|
||||
const handleRemoveImage = (type: ImageType) => {
|
||||
const updates: any = {
|
||||
logo_url: images.logo?.url,
|
||||
banner_image_id: images.banner?.id,
|
||||
banner_image_url: images.banner?.url,
|
||||
card_image_id: images.card?.id,
|
||||
card_image_url: images.card?.url
|
||||
};
|
||||
|
||||
if (type === 'logo') {
|
||||
updates.logo_url = undefined;
|
||||
} else if (type === 'banner') {
|
||||
updates.banner_image_id = undefined;
|
||||
updates.banner_image_url = undefined;
|
||||
} else if (type === 'card') {
|
||||
updates.card_image_id = undefined;
|
||||
updates.card_image_url = undefined;
|
||||
}
|
||||
|
||||
onImagesChange(updates);
|
||||
};
|
||||
|
||||
const renderImageSlot = (type: ImageType) => {
|
||||
if (type === 'logo' && !showLogo) return null;
|
||||
|
||||
const spec = IMAGE_SPECS[type];
|
||||
const currentImage = type === 'logo' ? images.logo : type === 'banner' ? images.banner : images.card;
|
||||
const hasImage = currentImage?.url;
|
||||
|
||||
if (activeSlot === type) {
|
||||
return (
|
||||
<Card key={type} className="p-4">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="text-base font-semibold">{spec.label}</Label>
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{spec.aspect} • {spec.dimensions}
|
||||
</Badge>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setActiveSlot(null)}
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{spec.description}</p>
|
||||
<UppyPhotoUpload
|
||||
onUploadComplete={(urls) => handleUploadComplete(type, urls)}
|
||||
maxFiles={1}
|
||||
variant="compact"
|
||||
allowedFileTypes={['image/jpeg', 'image/jpg', 'image/png', 'image/webp']}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card key={type} className="overflow-hidden">
|
||||
{hasImage ? (
|
||||
<div className="relative aspect-[16/9] bg-muted">
|
||||
<img
|
||||
src={currentImage.url}
|
||||
alt={`${spec.label} preview`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/50 opacity-0 hover:opacity-100 transition-opacity flex items-center justify-center gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => setActiveSlot(type)}
|
||||
>
|
||||
<ImagePlus className="w-4 h-4 mr-2" />
|
||||
Replace
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={() => handleRemoveImage(type)}
|
||||
>
|
||||
<X className="w-4 h-4 mr-2" />
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
<Badge className="absolute top-2 left-2">{spec.label}</Badge>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveSlot(type)}
|
||||
className="w-full aspect-[16/9] flex flex-col items-center justify-center gap-2 bg-muted hover:bg-muted/80 transition-colors"
|
||||
>
|
||||
<ImageIcon className="w-8 h-8 text-muted-foreground" />
|
||||
<div className="text-center">
|
||||
<p className="text-sm font-medium">{spec.label}</p>
|
||||
<p className="text-xs text-muted-foreground">{spec.dimensions}</p>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="text-base">Images</Label>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
Recommended formats: JPG, PNG, WebP
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{showLogo && renderImageSlot('logo')}
|
||||
{renderImageSlot('banner')}
|
||||
{renderImageSlot('card')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -77,6 +77,10 @@ export type Database = {
|
||||
companies: {
|
||||
Row: {
|
||||
average_rating: number | null
|
||||
banner_image_id: string | null
|
||||
banner_image_url: string | null
|
||||
card_image_id: string | null
|
||||
card_image_url: string | null
|
||||
company_type: string
|
||||
created_at: string
|
||||
description: string | null
|
||||
@@ -93,6 +97,10 @@ export type Database = {
|
||||
}
|
||||
Insert: {
|
||||
average_rating?: number | null
|
||||
banner_image_id?: string | null
|
||||
banner_image_url?: string | null
|
||||
card_image_id?: string | null
|
||||
card_image_url?: string | null
|
||||
company_type: string
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
@@ -109,6 +117,10 @@ export type Database = {
|
||||
}
|
||||
Update: {
|
||||
average_rating?: number | null
|
||||
banner_image_id?: string | null
|
||||
banner_image_url?: string | null
|
||||
card_image_id?: string | null
|
||||
card_image_url?: string | null
|
||||
company_type?: string
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
@@ -806,6 +818,10 @@ export type Database = {
|
||||
}
|
||||
ride_models: {
|
||||
Row: {
|
||||
banner_image_id: string | null
|
||||
banner_image_url: string | null
|
||||
card_image_id: string | null
|
||||
card_image_url: string | null
|
||||
category: string
|
||||
created_at: string
|
||||
description: string | null
|
||||
@@ -818,6 +834,10 @@ export type Database = {
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
banner_image_id?: string | null
|
||||
banner_image_url?: string | null
|
||||
card_image_id?: string | null
|
||||
card_image_url?: string | null
|
||||
category: string
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
@@ -830,6 +850,10 @@ export type Database = {
|
||||
updated_at?: string
|
||||
}
|
||||
Update: {
|
||||
banner_image_id?: string | null
|
||||
banner_image_url?: string | null
|
||||
card_image_id?: string | null
|
||||
card_image_url?: string | null
|
||||
category?: string
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
-- Add image fields to companies table
|
||||
ALTER TABLE public.companies
|
||||
ADD COLUMN IF NOT EXISTS banner_image_id TEXT,
|
||||
ADD COLUMN IF NOT EXISTS banner_image_url TEXT,
|
||||
ADD COLUMN IF NOT EXISTS card_image_id TEXT,
|
||||
ADD COLUMN IF NOT EXISTS card_image_url TEXT;
|
||||
|
||||
-- Add image fields to ride_models table
|
||||
ALTER TABLE public.ride_models
|
||||
ADD COLUMN IF NOT EXISTS banner_image_id TEXT,
|
||||
ADD COLUMN IF NOT EXISTS banner_image_url TEXT,
|
||||
ADD COLUMN IF NOT EXISTS card_image_id TEXT,
|
||||
ADD COLUMN IF NOT EXISTS card_image_url TEXT;
|
||||
Reference in New Issue
Block a user