'use client' import { useState, useEffect } from 'react' import { useRouter } from 'next/navigation' import { api } from '@/lib/api' import type { Tag, Feature } from '@/types' /** Пропсы компонента формы создания места/студии */ interface PlaceFormProps { /** Тип создаваемого объекта: место или студия */ type: 'place' | 'studio' } /** * Форма создания нового места или студии. * Собирает название, описание, адрес, координаты, теги, характеристики и обложку. * Для студий дополнительно запрашивает цену за час и минимальное количество часов. * Перед сохранением загружает обложку через presigned URL. * @param type - Тип объекта: 'place' — место, 'studio' — студия */ export default function PlaceForm({ type }: PlaceFormProps) { const router = useRouter() const [tags, setTags] = useState([]) const [features, setFeatures] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [title, setTitle] = useState('') const [description, setDescription] = useState('') const [address, setAddress] = useState('') const [accessInfo, setAccessInfo] = useState('') const [selectedTags, setSelectedTags] = useState([]) const [selectedFeatures, setSelectedFeatures] = useState([]) const [lat, setLat] = useState('55.7558') const [lng, setLng] = useState('37.6173') const [hourlyRate, setHourlyRate] = useState('') const [minHours, setMinHours] = useState('1') const [coverFile, setCoverFile] = useState(null) const [coverPreview, setCoverPreview] = useState('') useEffect(() => { api.get('/tags').then(setTags).catch(() => {}) api.get('/features').then(setFeatures).catch(() => {}) }, []) /** Освобождает blob-URL обложки при размонтировании или смене preview */ useEffect(() => { return () => { if (coverPreview) { URL.revokeObjectURL(coverPreview) } } }, [coverPreview]) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setLoading(true) setError('') try { let coverImage: string | undefined if (coverFile) { const presigned = await api.post<{ upload_url: string; file_url: string; fields: Record }>( '/upload/presigned-url', { content_type: coverFile.type, max_size_mb: 10 } ) const formData = new FormData() Object.entries(presigned.fields).forEach(([k, v]) => formData.append(k, v)) formData.append('file', coverFile) await fetch(presigned.upload_url, { method: 'POST', body: formData }) coverImage = presigned.file_url } const body: Record = { type, title, description: description || undefined, address: address || undefined, lat: parseFloat(lat), lng: parseFloat(lng), access_info: accessInfo || undefined, tags: selectedTags, features: selectedFeatures, cover_image: coverImage, } if (type === 'studio') { body.hourly_rate = hourlyRate ? parseInt(hourlyRate) : undefined body.min_hours = parseInt(minHours) || 1 body.currency = 'RUB' } await api.post('/places', body) router.push('/') } catch (err: any) { setError(err.message || 'Ошибка при создании') } finally { setLoading(false) } } const toggleTag = (id: string) => { setSelectedTags((prev) => prev.includes(id) ? prev.filter((t) => t !== id) : [...prev, id] ) } const toggleFeature = (id: string) => { setSelectedFeatures((prev) => prev.includes(id) ? prev.filter((f) => f !== id) : [...prev, id] ) } return (

{type === 'studio' ? 'Добавить студию' : 'Добавить место'}

{error &&

{error}

}
setTitle(e.target.value)} className="w-full rounded-lg border border-white/10 bg-white/5 px-4 py-3 text-white outline-none focus:border-green-500" required />