import { useState } from 'react'; import { useStore } from '../store'; import { useNavigate } from 'react-router-dom'; import { Camera, MapPin, X, Loader2, Check } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { CameraCapture } from './CameraCapture'; import { INDIA_DATA } from '../data/india-data'; const INDIAN_STATES = Object.keys(INDIA_DATA); export function AddListing() { const { currentUser } = useStore(); const navigate = useNavigate(); const [cropName, setCropName] = useState(''); const [quantity, setQuantity] = useState(''); const [price, setPrice] = useState(''); const [imageFiles, setImageFiles] = useState([]); const [imagePreviewUrls, setImagePreviewUrls] = useState([]); const [showCamera, setShowCamera] = useState(false); const [location, setLocation] = useState(null); const [locating, setLocating] = useState(false); const [nearestCity, setNearestCity] = useState(''); const [district, setDistrict] = useState(''); const [state, setState] = useState(''); const [pincode, setPincode] = useState(''); const [showStateList, setShowStateList] = useState(false); const [filteredStates, setFilteredStates] = useState(INDIAN_STATES); const [showDistrictList, setShowDistrictList] = useState(false); const [filteredDistricts, setFilteredDistricts] = useState([]); const [error, setError] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const handleCapture = (file, dataUrl) => { if (imageFiles.length >= 5) { setError('You can only snap up to 5 photos.'); setShowCamera(false); return; } setImageFiles(prev => [...prev, file]); setImagePreviewUrls(prev => [...prev, dataUrl]); setError(''); setShowCamera(false); }; const removeImage = (index) => { setImageFiles(prev => prev.filter((_, i) => i !== index)); setImagePreviewUrls(prev => prev.filter((_, i) => i !== index)); }; const getLocation = () => { setLocating(true); setError(''); if (!('geolocation' in navigator)) { setError('Geolocation is not supported by your browser.'); setLocating(false); return; } const options = { enableHighAccuracy: true, timeout: 15000, maximumAge: 0 }; navigator.geolocation.getCurrentPosition( (position) => { setLocation({ lat: position.coords.latitude, lng: position.coords.longitude, address: `Precise GPS: ${position.coords.latitude.toFixed(4)}, ${position.coords.longitude.toFixed(4)}` }); setLocating(false); }, (err) => { console.error("Location Error:", err); let msg = 'Failed to get precise location.'; if (err.code === 1) msg = 'Location permission denied. Please allow location access in your browser settings.'; else if (err.code === 3) msg = 'Location request timed out. Please try again in an open area.'; setError(msg); // Fallback to a default if absolutely necessary, but notify the user setLocation({ lat: 23.0225, lng: 72.5714, address: 'Approximate Network Location' }); setLocating(false); }, options ); }; const handleSubmit = async (e) => { e.preventDefault(); if (imageFiles.length < 2) { setError('Please snap at least 2 live photos of your crop.'); return; } if (!location) { setError('Please provide your location.'); return; } setIsSubmitting(true); setError(''); try { const formData = new FormData(); formData.append('sellerId', currentUser.id); formData.append('sellerName', currentUser.name); formData.append('cropName', cropName); formData.append('quantity', quantity); formData.append('price', price); formData.append('location', JSON.stringify(location)); formData.append('nearestCity', nearestCity); formData.append('district', district); formData.append('state', state); formData.append('pincode', pincode); imageFiles.forEach(file => { formData.append('images', file); }); const res = await fetch('/api/listings', { method: 'POST', body: formData, }); const data = await res.json(); if (!res.ok) { throw new Error(data.error || 'Failed to publish listing'); } navigate('/seller'); } catch (err) { console.error(err); setError(err.message || 'An error occurred during upload. Please try again.'); } finally { setIsSubmitting(false); } }; return ( {showCamera && ( setShowCamera(false)} onCapture={handleCapture} /> )}

List Your Crop

Take live photos directly from the app.

{error && (
{error}
)}
setCropName(e.target.value)} required disabled={isSubmitting} />
setQuantity(e.target.value)} required disabled={isSubmitting} />
setPrice(e.target.value)} disabled={isSubmitting} />
{imagePreviewUrls.map((img, idx) => ( preview ))} {imageFiles.length < 5 && (
!isSubmitting && setShowCamera(true)} style={{ border: '2px dashed var(--border-color)', borderRadius: '12px', aspectRatio: '1', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', cursor: isSubmitting ? 'not-allowed' : 'pointer', background: 'var(--surface-light)', gap: '8px', opacity: isSubmitting ? 0.5 : 1 }} > Snap Feed
)}
{ const val = e.target.value; setState(val); setFilteredStates(INDIAN_STATES.filter(s => s.toLowerCase().includes(val.toLowerCase()))); setShowStateList(true); setDistrict(''); }} onFocus={() => setShowStateList(true)} disabled={isSubmitting} required /> {showStateList && filteredStates.length > 0 && ( {filteredStates.map(s => (
{ setState(s); setShowStateList(false); setDistrict(''); setFilteredDistricts(INDIA_DATA[s] || []); }} style={{ padding: '12px 16px', cursor: 'pointer', borderBottom: '1px solid var(--border-color)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} onMouseEnter={e => e.currentTarget.style.background = 'var(--primary-glow)'} onMouseLeave={e => e.currentTarget.style.background = 'transparent'} > {s} {state === s && }
))}
)}
{showStateList &&
setShowStateList(false)} style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 105 }} />}
{ const val = e.target.value; setDistrict(val); const ds = INDIA_DATA[state] || []; setFilteredDistricts(ds.filter(d => d.toLowerCase().includes(val.toLowerCase()))); setShowDistrictList(true); }} onFocus={() => { if (state) { const ds = INDIA_DATA[state] || []; setFilteredDistricts(ds.filter(d => d.toLowerCase().includes(district.toLowerCase()))); setShowDistrictList(true); } }} disabled={isSubmitting || !state} required /> {showDistrictList && filteredDistricts.length > 0 && ( {filteredDistricts.map(d => (
{ setDistrict(d); setShowDistrictList(false); }} style={{ padding: '12px 16px', cursor: 'pointer', borderBottom: '1px solid var(--border-color)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} onMouseEnter={e => e.currentTarget.style.background = 'var(--primary-glow)'} onMouseLeave={e => e.currentTarget.style.background = 'transparent'} > {d} {district === d && }
))}
)}
{showDistrictList &&
setShowDistrictList(false)} style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 105 }} />}
setNearestCity(e.target.value)} disabled={isSubmitting} required />
setPincode(e.target.value)} disabled={isSubmitting} required />
{location ? 'Precise Location Captured' : (locating ? 'Capturing GPS...' : 'Get Primary Location (GPS)')} {location && {location.address}}
{locating && }
); }