/**
* SubmitRidingPhoto Component
*
* Modal form for submitting a riding cover photo
*/
'use client';
import { useState, useRef } from 'react';
import { X, Upload, Loader2, Camera } from 'lucide-react';
import { Button } from '@/components/ui/button';
import Image from 'next/image';
interface SubmitRidingPhotoProps {
ridingName: string;
ridingId?: string;
isOpen: boolean;
onClose: () => void;
onSuccess?: () => void;
}
export function SubmitRidingPhoto({
ridingName,
ridingId,
isOpen,
onClose,
onSuccess,
}: SubmitRidingPhotoProps) {
const [file, setFile] = useState<File | null>(null);
const [preview, setPreview] = useState<string | null>(null);
const [caption, setCaption] = useState('');
const [uploading, setUploading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
if (!isOpen) return null;
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFile = e.target.files?.[0];
if (!selectedFile) return;
// Validate file type
const validTypes = ['image/jpeg', 'image/png', 'image/webp'];
if (!validTypes.includes(selectedFile.type)) {
setError('Please select a JPG, PNG, or WebP image');
return;
}
// Validate file size (5MB)
if (selectedFile.size > 5 * 1024 * 1024) {
setError('Image must be smaller than 5MB');
return;
}
setFile(selectedFile);
setError(null);
// Create preview
const reader = new FileReader();
reader.onload = (e) => {
setPreview(e.target?.result as string);
};
reader.readAsDataURL(selectedFile);
};
const handleSubmit = async () => {
if (!file) {
setError('Please select an image');
return;
}
setUploading(true);
setError(null);
try {
const formData = new FormData();
formData.append('file', file);
formData.append('ridingName', ridingName);
if (ridingId) {
formData.append('ridingId', ridingId);
}
if (caption.trim()) {
formData.append('caption', caption.trim());
}
const response = await fetch('/api/riding-photos/submit', {
method: 'POST',
body: formData,
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || 'Failed to submit photo');
}
setSuccess(true);
onSuccess?.();
// Close modal after showing success
setTimeout(() => {
handleClose();
}, 2000);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to submit photo');
} finally {
setUploading(false);
}
};
const handleClose = () => {
setFile(null);
setPreview(null);
setCaption('');
setError(null);
setSuccess(false);
onClose();
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/50"
onClick={handleClose}
/>
{/* Modal */}
<div className="relative z-10 w-full max-w-md mx-4 bg-background rounded-lg shadow-xl">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b">
<h2 className="text-lg font-semibold">Submit a photo for {ridingName}</h2>
<button
onClick={handleClose}
className="p-1 text-muted-foreground hover:text-foreground transition-colors"
>
<X className="h-5 w-5" />
</button>
</div>
{/* Content */}
<div className="p-4 space-y-4">
{success ? (
<div className="text-center py-8">
<div className="mx-auto w-12 h-12 rounded-full bg-green-100 flex items-center justify-center mb-4">
<svg className="w-6 h-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 className="text-lg font-medium text-green-800">Photo submitted!</h3>
<p className="mt-1 text-sm text-muted-foreground">
Your photo will appear after it's reviewed by a moderator.
</p>
</div>
) : (
<>
{/* File Upload Area */}
<div
onClick={() => fileInputRef.current?.click()}
className={`
relative border-2 border-dashed rounded-lg p-6 cursor-pointer transition-colors
${preview ? 'border-accent-red' : 'border-muted hover:border-muted-foreground'}
`}
>
<input
ref={fileInputRef}
type="file"
accept="image/jpeg,image/png,image/webp"
onChange={handleFileSelect}
className="hidden"
/>
{preview ? (
<div className="relative aspect-video">
<Image
src={preview}
alt="Preview"
fill
className="object-cover rounded"
/>
<div className="absolute inset-0 flex items-center justify-center bg-black/30 opacity-0 hover:opacity-100 transition-opacity rounded">
<Camera className="h-8 w-8 text-white" />
</div>
</div>
) : (
<div className="text-center">
<Upload className="mx-auto h-12 w-12 text-muted-foreground" />
<p className="mt-2 text-sm text-muted-foreground">
Click to select an image
</p>
<p className="mt-1 text-xs text-muted-foreground">
JPG, PNG, or WebP. Max 5MB.
</p>
</div>
)}
</div>
{/* Caption */}
<div>
<label htmlFor="caption" className="block text-sm font-medium mb-1">
Caption (optional)
</label>
<input
type="text"
id="caption"
value={caption}
onChange={(e) => setCaption(e.target.value)}
placeholder="e.g., Parliament Hill at sunset"
maxLength={200}
className="w-full px-3 py-2 border rounded-md bg-background focus:ring-2 focus:ring-accent-red focus:border-accent-red"
/>
<p className="mt-1 text-xs text-muted-foreground">
{caption.length}/200 characters
</p>
</div>
{/* Guidelines */}
<div className="text-xs text-muted-foreground space-y-1">
<p className="font-medium">Photo guidelines:</p>
<ul className="list-disc list-inside space-y-0.5 ml-2">
<li>Photo should be of or from your riding</li>
<li>Landscape orientation works best</li>
<li>No copyrighted images without permission</li>
<li>Photos are reviewed before appearing</li>
</ul>
</div>
{/* Error Message */}
{error && (
<div className="p-3 bg-destructive/10 border border-destructive/20 rounded-md">
<p className="text-sm text-destructive">{error}</p>
</div>
)}
</>
)}
</div>
{/* Footer */}
{!success && (
<div className="flex gap-3 p-4 border-t">
<Button
variant="outline"
onClick={handleClose}
disabled={uploading}
className="flex-1"
>
Cancel
</Button>
<Button
onClick={handleSubmit}
disabled={!file || uploading}
className="flex-1 bg-accent-red hover:bg-accent-red/90"
>
{uploading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Uploading...
</>
) : (
'Submit photo'
)}
</Button>
</div>
)}
</div>
</div>
);
}