mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 19:11:13 -05:00
Implement photo upload enhancements
This commit is contained in:
@@ -10,6 +10,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Upload, X, Eye, Loader2 } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { DragDropZone } from './DragDropZone';
|
||||
|
||||
interface UppyPhotoUploadProps {
|
||||
onUploadComplete?: (urls: string[]) => void;
|
||||
@@ -25,6 +26,8 @@ interface UppyPhotoUploadProps {
|
||||
disabled?: boolean;
|
||||
showPreview?: boolean;
|
||||
size?: 'default' | 'compact' | 'large';
|
||||
enableDragDrop?: boolean;
|
||||
showUploadModal?: boolean;
|
||||
}
|
||||
|
||||
interface CloudflareResponse {
|
||||
@@ -59,6 +62,8 @@ export function UppyPhotoUpload({
|
||||
disabled = false,
|
||||
showPreview = true,
|
||||
size = 'default',
|
||||
enableDragDrop = true,
|
||||
showUploadModal = true,
|
||||
}: UppyPhotoUploadProps) {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [uploadedImages, setUploadedImages] = useState<string[]>([]);
|
||||
@@ -167,45 +172,44 @@ export function UppyPhotoUpload({
|
||||
setUploadProgress(Math.round(progress));
|
||||
});
|
||||
|
||||
// Handle upload success
|
||||
uppy.on('upload-success', async (file, response) => {
|
||||
try {
|
||||
const cloudflareId = file?.meta?.cloudflareId as string;
|
||||
if (!cloudflareId) {
|
||||
throw new Error('Missing Cloudflare ID');
|
||||
}
|
||||
|
||||
// Poll for upload completion
|
||||
let attempts = 0;
|
||||
const maxAttempts = 30; // 30 seconds max
|
||||
let imageData: UploadSuccessResponse | null = null;
|
||||
|
||||
while (attempts < maxAttempts) {
|
||||
const statusResponse = await supabase.functions.invoke('upload-image', {
|
||||
body: null,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (!statusResponse.error && statusResponse.data) {
|
||||
const status: UploadSuccessResponse = statusResponse.data;
|
||||
if (status.uploaded && status.urls) {
|
||||
imageData = status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (imageData?.urls) {
|
||||
setUploadedImages(prev => [...prev, imageData.urls!.public]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Upload post-processing failed:', error);
|
||||
onUploadError?.(error as Error);
|
||||
// Handle upload success
|
||||
uppy.on('upload-success', async (file, response) => {
|
||||
try {
|
||||
const cloudflareId = file?.meta?.cloudflareId as string;
|
||||
if (!cloudflareId) {
|
||||
throw new Error('Missing Cloudflare ID');
|
||||
}
|
||||
});
|
||||
|
||||
// Poll for upload completion with the correct ID
|
||||
let attempts = 0;
|
||||
const maxAttempts = 30; // 30 seconds max
|
||||
let imageData: UploadSuccessResponse | null = null;
|
||||
|
||||
while (attempts < maxAttempts) {
|
||||
const statusResponse = await supabase.functions.invoke('upload-image', {
|
||||
body: { id: cloudflareId },
|
||||
});
|
||||
|
||||
if (!statusResponse.error && statusResponse.data) {
|
||||
const status: UploadSuccessResponse = statusResponse.data;
|
||||
if (status.uploaded && status.urls) {
|
||||
imageData = status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (imageData?.urls) {
|
||||
setUploadedImages(prev => [...prev, imageData.urls!.public]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Upload post-processing failed:', error);
|
||||
onUploadError?.(error as Error);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle upload error
|
||||
uppy.on('upload-error', (file, error, response) => {
|
||||
@@ -263,6 +267,20 @@ export function UppyPhotoUpload({
|
||||
};
|
||||
}, [maxFiles, maxSizeMB, allowedFileTypes, metadata, variant, disabled, onUploadStart, onUploadComplete, onUploadError, toast, uploadedImages, size]);
|
||||
|
||||
const handleDragDropFiles = async (files: File[]) => {
|
||||
if (!uppyRef.current || disabled) return;
|
||||
|
||||
// Add files to Uppy and start upload
|
||||
files.forEach((file) => {
|
||||
uppyRef.current?.addFile({
|
||||
source: 'drag-drop',
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
data: file,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const removeImage = (index: number) => {
|
||||
const newUrls = uploadedImages.filter((_, i) => i !== index);
|
||||
setUploadedImages(newUrls);
|
||||
@@ -367,12 +385,50 @@ export function UppyPhotoUpload({
|
||||
);
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
if (enableDragDrop && !children) {
|
||||
return (
|
||||
<DragDropZone
|
||||
onFilesAdded={handleDragDropFiles}
|
||||
maxFiles={maxFiles}
|
||||
maxSizeMB={maxSizeMB}
|
||||
allowedFileTypes={allowedFileTypes}
|
||||
disabled={disabled}
|
||||
className="min-h-[200px]"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
{renderUploadTrigger()}
|
||||
{renderPreview()}
|
||||
</div>
|
||||
</DragDropZone>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{enableDragDrop && children ? (
|
||||
<DragDropZone
|
||||
onFilesAdded={handleDragDropFiles}
|
||||
maxFiles={maxFiles}
|
||||
maxSizeMB={maxSizeMB}
|
||||
allowedFileTypes={allowedFileTypes}
|
||||
disabled={disabled}
|
||||
>
|
||||
{renderUploadTrigger()}
|
||||
</DragDropZone>
|
||||
) : (
|
||||
renderUploadTrigger()
|
||||
)}
|
||||
{renderPreview()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn("space-y-4", className)}>
|
||||
{renderUploadTrigger()}
|
||||
{renderPreview()}
|
||||
{renderContent()}
|
||||
|
||||
{uppyRef.current && (
|
||||
{uppyRef.current && showUploadModal && (
|
||||
<DashboardModal
|
||||
uppy={uppyRef.current}
|
||||
open={isModalOpen}
|
||||
|
||||
Reference in New Issue
Block a user