import React, { useState, useEffect } from 'react'; import { X, Plus, Trash2 } from 'lucide-react'; import { DevOSSSession } from '../../types'; import { Building } from '../../types'; import { backendApi } from '../../services/apiClient'; interface Props { isOpen: boolean; onClose: () => void; onSuccess: () => void; } export const CreateOSSModal: React.FC = ({ isOpen, onClose, onSuccess }) => { const [formData, setFormData] = useState({ buildingId: '', address: '', startDate: '', endDate: '', quorumTotal: 0, type: 'extraordinary' as 'annual' | 'extraordinary', description: '', source: 'existing' as 'existing' | 'pipeline', agendaItems: [] as string[], }); const [buildings, setBuildings] = useState([]); const [pipelineAddresses, setPipelineAddresses] = useState<{id: string, address: string}[]>([]); const [loading, setLoading] = useState(false); const [loadingBuildings, setLoadingBuildings] = useState(true); useEffect(() => { if (isOpen) { fetchData(); } }, [isOpen]); const fetchData = async () => { try { setLoadingBuildings(true); // Загружаем существующие дома const buildingsData = await backendApi.getBuildings(); setBuildings(buildingsData); // Загружаем адреса из воронки try { const pipelineData = await backendApi.getDevelopmentPipeline(); setPipelineAddresses(pipelineData.map(p => ({ id: p.id, address: p.address }))); } catch (err) { console.warn('Failed to load pipeline addresses:', err); } } catch (error) { console.error('Error fetching data:', error); } finally { setLoadingBuildings(false); } }; if (!isOpen) return null; const handleSourceChange = (source: 'existing' | 'pipeline') => { setFormData({ ...formData, source, buildingId: '', address: '' }); }; const handleBuildingChange = (value: string) => { if (formData.source === 'existing') { const building = buildings.find(b => b.id === value); const totalArea = building?.passport?.general?.totalArea; setFormData({ ...formData, buildingId: value, address: building?.passport?.address || '', quorumTotal: totalArea && totalArea > 0 ? totalArea : 0 }); } else { const pipelineItem = pipelineAddresses.find(p => p.id === value); setFormData({ ...formData, buildingId: value, address: pipelineItem?.address || '', // Для объектов из воронки площадь не заполняется автоматически }); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); // Улучшенная валидация const address = formData.address?.trim(); const startDate = formData.startDate?.trim(); const endDate = formData.endDate?.trim() || defaultEndDate; const quorumTotal = formData.quorumTotal; if (!address || address.length === 0) { alert('Заполните обязательное поле: адрес'); return; } if (!startDate || startDate.length === 0) { alert('Заполните обязательное поле: дата начала'); return; } if (!endDate || endDate.length === 0) { alert('Заполните обязательное поле: дата окончания'); return; } if (!quorumTotal || quorumTotal <= 0) { alert('Заполните обязательное поле: общая площадь (должна быть больше 0)'); return; } if (new Date(endDate) <= new Date(startDate)) { alert('Дата окончания должна быть позже даты начала'); return; } try { setLoading(true); const agenda = formData.agendaItems.filter((t) => t.trim()).length > 0 ? formData.agendaItems.filter((t) => t.trim()) : undefined; const result = await backendApi.createDevelopmentOSS({ address, buildingId: formData.source === 'existing' ? formData.buildingId : null, building_id: formData.source === 'existing' ? formData.buildingId : null, startDate: startDate, start_date: startDate, endDate: endDate, end_date: endDate, quorumTotal, quorum_total: quorumTotal, quorumCurrent: 0, type: formData.type, status: 'planned' as const, description: formData.description?.trim() || null, agenda_items: agenda, agendaItems: agenda, } as any); console.log('OSS created successfully:', result); // Закрываем модальное окно и сбрасываем форму перед вызовом onSuccess // чтобы избежать проблем с обновлением состояния setFormData({ buildingId: '', address: '', startDate: '', endDate: '', quorumTotal: 0, type: 'extraordinary', description: '', source: 'existing', agendaItems: [], }); onClose(); // Вызываем onSuccess после небольшой задержки, чтобы модальное окно успело закрыться setTimeout(() => { window.dispatchEvent(new CustomEvent('mkd-oss-changed')); window.dispatchEvent(new CustomEvent('mkd-dev-summary-changed')); onSuccess(); }, 100); } catch (error: any) { console.error('Error creating OSS:', error); const errorMessage = error?.response?.data?.error || error?.message || error?.error || 'Ошибка при создании ОСС'; alert(`Ошибка при создании ОСС: ${errorMessage}`); } finally { setLoading(false); } }; // Вычисляем дату окончания по умолчанию (через 30 дней) const defaultEndDate = formData.startDate ? new Date(new Date(formData.startDate).getTime() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0] : ''; return (
e.stopPropagation()} >

Создать собрание собственников (ОСС)

{/* Выбор источника */}
{/* Выбор дома/адреса */}
{loadingBuildings ? (
Загрузка...
) : ( <> {formData.source === 'existing' ? ( ) : ( )} {formData.source === 'pipeline' && pipelineAddresses.length === 0 && (

Нет объектов в воронке. Сначала добавьте объект в воронку.

)} )}
{/* Адрес (автозаполняется, но можно редактировать) */}
setFormData({ ...formData, address: e.target.value })} placeholder="ул. Примерная, д. 1" className="w-full p-2.5 rounded-xl border border-slate-200 text-sm focus:ring-2 focus:ring-primary-500 outline-none" />
{/* Тип ОСС */}
{/* Дата начала */}
setFormData({ ...formData, startDate: e.target.value })} min={new Date().toISOString().split('T')[0]} className="w-full p-2.5 rounded-xl border border-slate-200 text-sm focus:ring-2 focus:ring-primary-500 outline-none" />
{/* Дата окончания */}
setFormData({ ...formData, endDate: e.target.value })} min={formData.startDate || new Date().toISOString().split('T')[0]} className="w-full p-2.5 rounded-xl border border-slate-200 text-sm focus:ring-2 focus:ring-primary-500 outline-none" /> {formData.startDate && !formData.endDate && (

Рекомендуемая дата: {defaultEndDate}

)}
{/* Общая площадь для кворума */}
{ const value = e.target.value; setFormData({ ...formData, quorumTotal: value ? parseFloat(value) : 0 }); }} placeholder="25000" className="w-full p-2.5 rounded-xl border border-slate-200 text-sm focus:ring-2 focus:ring-primary-500 outline-none" />

Для расчета кворума (50% + 1 м²). Минимум: 1 м²

{formData.quorumTotal <= 0 && formData.quorumTotal !== 0 && (

Площадь должна быть больше 0

)}
{/* Пункты повестки (голосование по каждому) */}

Добавьте пункты — в бюллетене можно будет указать «За» / «Против» / «Воздержался» по каждому.

{(formData.agendaItems.length === 0 ? [''] : formData.agendaItems).map((text, idx) => (
{idx + 1}. { const next = [...(formData.agendaItems.length ? formData.agendaItems : [''])]; next[idx] = e.target.value; setFormData({ ...formData, agendaItems: next }); }} placeholder="Формулировка пункта" className="flex-1 p-2 rounded-xl border border-slate-200 text-sm" />
))}
{/* Описание */}