mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 12:51:16 -05:00
feat: Implement Uppy photo upload
This commit is contained in:
194
src/components/upload/UppyPhotoSubmissionUpload.tsx
Normal file
194
src/components/upload/UppyPhotoSubmissionUpload.tsx
Normal file
@@ -0,0 +1,194 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { UppyPhotoUpload } from './UppyPhotoUpload';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { Camera } from 'lucide-react';
|
||||
|
||||
interface UppyPhotoSubmissionUploadProps {
|
||||
onSubmissionComplete?: () => void;
|
||||
parkId?: string;
|
||||
rideId?: string;
|
||||
}
|
||||
|
||||
export function UppyPhotoSubmissionUpload({
|
||||
onSubmissionComplete,
|
||||
parkId,
|
||||
rideId,
|
||||
}: UppyPhotoSubmissionUploadProps) {
|
||||
const [title, setTitle] = useState('');
|
||||
const [caption, setCaption] = useState('');
|
||||
const [uploadedUrls, setUploadedUrls] = useState<string[]>([]);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const { user } = useAuth();
|
||||
const { toast } = useToast();
|
||||
|
||||
const handleUploadComplete = (urls: string[]) => {
|
||||
setUploadedUrls(urls);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!user) {
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
title: 'Authentication Required',
|
||||
description: 'Please sign in to submit photos.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (uploadedUrls.length === 0) {
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
title: 'No Photos',
|
||||
description: 'Please upload at least one photo before submitting.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!title.trim()) {
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
title: 'Title Required',
|
||||
description: 'Please provide a title for your photo submission.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
const submissionData = {
|
||||
user_id: user.id,
|
||||
submission_type: 'photo',
|
||||
content: {
|
||||
title: title.trim(),
|
||||
caption: caption.trim(),
|
||||
photos: uploadedUrls.map((url, index) => ({
|
||||
url,
|
||||
order: index,
|
||||
})),
|
||||
context: {
|
||||
park_id: parkId,
|
||||
ride_id: rideId,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { error } = await supabase
|
||||
.from('content_submissions')
|
||||
.insert(submissionData);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
toast({
|
||||
title: 'Submission Successful',
|
||||
description: 'Your photos have been submitted for review. Thank you for contributing!',
|
||||
});
|
||||
|
||||
// Reset form
|
||||
setTitle('');
|
||||
setCaption('');
|
||||
setUploadedUrls([]);
|
||||
onSubmissionComplete?.();
|
||||
} catch (error) {
|
||||
console.error('Submission error:', error);
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
title: 'Submission Failed',
|
||||
description: 'There was an error submitting your photos. Please try again.',
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const metadata = {
|
||||
submissionType: 'photo',
|
||||
parkId,
|
||||
rideId,
|
||||
userId: user?.id,
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="p-6">
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Camera className="h-5 w-5 text-primary" />
|
||||
<h3 className="text-lg font-semibold">Submit Photos</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="title">Title *</Label>
|
||||
<Input
|
||||
id="title"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="Give your photos a descriptive title"
|
||||
maxLength={100}
|
||||
required
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{title.length}/100 characters
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="caption">Caption</Label>
|
||||
<Textarea
|
||||
id="caption"
|
||||
value={caption}
|
||||
onChange={(e) => setCaption(e.target.value)}
|
||||
placeholder="Add a description or story about these photos..."
|
||||
maxLength={500}
|
||||
rows={3}
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{caption.length}/500 characters
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>Photos</Label>
|
||||
<UppyPhotoUpload
|
||||
onUploadComplete={handleUploadComplete}
|
||||
maxFiles={10}
|
||||
maxSizeMB={25}
|
||||
metadata={metadata}
|
||||
variant="public"
|
||||
className="mt-2"
|
||||
/>
|
||||
{uploadedUrls.length > 0 && (
|
||||
<p className="text-sm text-muted-foreground mt-2">
|
||||
{uploadedUrls.length} photo(s) uploaded and ready to submit
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
disabled={isSubmitting || uploadedUrls.length === 0 || !title.trim()}
|
||||
className="flex-1"
|
||||
>
|
||||
{isSubmitting ? 'Submitting...' : 'Submit Photos'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground">
|
||||
All photo submissions are reviewed before being published. Please ensure your photos
|
||||
follow our community guidelines and are appropriate for all audiences.
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user