// screens-misc.jsx — create listing, alerts, referral, admin // Sous-composant DÉFINI EN DEHORS pour éviter le bug focus React function ListingField({ label, children }) { return (
{label}
{children}
); } function CreateListingScreen({ lang, t, onBack, onPublish }) { const me = (window.TrustaAuth && window.TrustaAuth.getUser()) || null; const [kind, setKind] = React.useState('demand'); // demand = je cherche, offer = je propose const [cats, setCats] = React.useState(['housekeeping']); // multi-sélection const [title, setTitle] = React.useState(''); const [desc, setDesc] = React.useState(''); const [cities, setCities] = React.useState(me && me.city ? [me.city] : []); // chips const [cityInput, setCityInput] = React.useState(''); const [rate, setRate] = React.useState(''); const [unit, setUnit] = React.useState('hour'); const [photos, setPhotos] = React.useState([]); // [{url, name}] const [uploading, setUploading] = React.useState(false); const [publishing, setPublishing] = React.useState(false); const [error, setError] = React.useState(null); const fileRef = React.useRef(null); const inputStyle = { height: 48, borderRadius: 12, background: COLORS.card, border: `1px solid ${COLORS.borderSoft}`, padding: '0 14px', color: '#fff', fontFamily: 'Inter', fontSize: 14, outline: 'none', boxSizing: 'border-box', width: '100%', }; const onPickFiles = async (e) => { const files = Array.from(e.target.files || []); if (fileRef.current) fileRef.current.value = ''; // reset tout de suite pour pouvoir re-sélectionner if (!files.length) return; if (photos.length + files.length > 6) { setError(lang === 'ka' ? 'მაქს. 6 ფოტო' : 'Max 6 photos'); return; } setError(null); setUploading(true); const uploaded = []; try { for (const f of files) { if (!window.API_AVAILABLE) { uploaded.push({ url: URL.createObjectURL(f), name: f.name }); continue; } const res = await window.TrustaAPI.uploadListingPhoto(f); uploaded.push({ url: res.url, name: f.name }); } // ⚠️ functional update : on ne touche PAS la state directement setPhotos(prev => [...prev, ...uploaded]); } catch (err) { console.error('upload failed', err); // on garde quand même celles déjà uploadées avec succès if (uploaded.length) setPhotos(prev => [...prev, ...uploaded]); setError(window.errorMessage ? window.errorMessage(err, lang) : (err.message || 'Upload error')); } finally { setUploading(false); } }; const removePhoto = (i) => { setPhotos(prev => prev.filter((_, idx) => idx !== i)); }; // === Helpers villes (chips) === const addCity = (v) => { const c = (v || cityInput).trim(); if (!c || c.length < 2) return; if (cities.includes(c)) { setCityInput(''); return; } if (cities.length >= 10) { setError(lang === 'ka' ? 'მაქს. 10 ქალაქი' : 'Max 10 villes'); return; } setCities([...cities, c]); setCityInput(''); }; const removeCity = (i) => { const next = [...cities]; next.splice(i, 1); setCities(next); }; const toggleCat = (id) => { if (cats.includes(id)) { if (cats.length === 1) return; // garde au moins une cat sélectionnée setCats(cats.filter(c => c !== id)); } else { if (cats.length >= 5) { setError(lang === 'ka' ? 'მაქს. 5 კატეგორია' : 'Max 5 catégories'); return; } setCats([...cats, id]); } }; const submit = async () => { if (publishing) return; setError(null); // Si une ville est tapée mais pas validée par Entrée → la prendre quand même let finalCities = [...cities]; const pending = cityInput.trim(); if (pending.length >= 2 && !finalCities.includes(pending)) finalCities.push(pending); if (title.trim().length < 4) { setError(lang === 'ka' ? 'სათაური ძალიან მოკლეა' : 'Titre trop court (4+ caractères)'); return; } if (finalCities.length === 0) { setError(lang === 'ka' ? 'მიუთითეთ მინ. 1 ქალაქი' : 'Indique au moins une ville'); return; } if (cats.length === 0) { setError(lang === 'ka' ? 'აირჩიე კატეგორია' : 'Choisis une catégorie'); return; } if (!window.API_AVAILABLE) { onPublish(); return; } setPublishing(true); try { await window.TrustaAPI.createListing({ kind, categories: cats, title: title.trim(), description: desc.trim() || undefined, cities: finalCities, price_amount: rate ? parseFloat(rate) : undefined, price_unit: unit, photos: photos.map(p => p.url), }); onPublish(); } catch (err) { console.error('createListing failed', err); setError(window.errorMessage ? window.errorMessage(err, lang) : (err.message || 'Erreur')); setPublishing(false); } }; return (
{/* Kind toggle */}
{[ { id: 'demand', label: t('kind_demand') }, { id: 'offer', label: t('kind_offer') }, ].map(k => { const a = kind === k.id; return ( ); })}
{/* Photos */}
{photos.map((p, i) => (
))} {photos.length < 6 && ( )}
setTitle(e.target.value)} placeholder={t('title_ph')} style={inputStyle} maxLength={200} />