import React, { useState, useEffect, useCallback } from 'react'; import { DevOSSSession } from '../../types'; import { Vote, Plus, FileText, CheckCircle2, AlertCircle, Calendar, UserPlus, Pencil, Filter } from 'lucide-react'; import { backendApi } from '../../services/apiClient'; import { readCache, saveCache } from '../../hooks/useCachedFetch'; import { REFRESH_EVENTS } from '../../constants/refreshEvents'; import { CreateOSSModal } from './CreateOSSModal'; import { EditOSSModal } from './EditOSSModal'; import { AddBallotModal } from './AddBallotModal'; import { BulkBallotModal } from './BulkBallotModal'; import { OSSRegistryModal } from './OSSRegistryModal'; const CACHE_KEY = 'mkd_dev_oss_cache'; type OSSStatusFilter = '' | 'planned' | 'active' | 'completed'; export const OSSMaster: React.FC = () => { const cached = readCache(CACHE_KEY, []); const [ossSessions, setOssSessions] = useState(cached); const [loading, setLoading] = useState(cached.length === 0); const [statusFilter, setStatusFilter] = useState(''); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [isEditModalOpen, setIsEditModalOpen] = useState(false); const [editingSession, setEditingSession] = useState(null); const [isBallotModalOpen, setIsBallotModalOpen] = useState(false); const [isBulkBallotModalOpen, setIsBulkBallotModalOpen] = useState(false); const [isRegistryModalOpen, setIsRegistryModalOpen] = useState(false); const [selectedOSS, setSelectedOSS] = useState<{id: string, address: string} | null>(null); const fetchOSS = useCallback(async (showSpinner = true) => { try { if (showSpinner && cached.length === 0) setLoading(true); const params = statusFilter ? { status: statusFilter } : undefined; const data = await backendApi.getDevelopmentOSS(params); const formattedData = Array.isArray(data) ? data : []; setOssSessions(formattedData); if (!statusFilter) saveCache(CACHE_KEY, formattedData); } catch (error) { console.error('Error fetching OSS sessions:', error); setOssSessions([]); } finally { setLoading(false); } }, [statusFilter]); useEffect(() => { fetchOSS(); }, [fetchOSS]); useEffect(() => { const onRefresh = () => fetchOSS(false); window.addEventListener(REFRESH_EVENTS.oss, onRefresh); return () => window.removeEventListener(REFRESH_EVENTS.oss, onRefresh); }, [fetchOSS]); useEffect(() => { const interval = setInterval(() => fetchOSS(false), 10 * 1000); return () => clearInterval(interval); }, [fetchOSS]); const handleCreateOSS = () => { setIsCreateModalOpen(true); }; const handleSubmitBallot = (ossId: string, ossAddress: string) => { setSelectedOSS({ id: ossId, address: ossAddress }); setIsBallotModalOpen(true); }; const handleBulkBallot = (ossId: string, ossAddress: string) => { setSelectedOSS({ id: ossId, address: ossAddress }); setIsBulkBallotModalOpen(true); }; const handleViewRegistry = (ossId: string, ossAddress: string) => { setSelectedOSS({ id: ossId, address: ossAddress }); setIsRegistryModalOpen(true); }; const handleEditOSS = (oss: DevOSSSession) => { setEditingSession(oss); setIsEditModalOpen(true); }; return (

ОСС

{loading ? (

Загрузка...

) : ossSessions.length === 0 ? (

{statusFilter ? 'Нет ОСС по выбранному фильтру' : 'Нет ОСС'}

) : ( ossSessions.map(oss => { const percent = Math.min(100, Math.round((oss.quorumCurrent / oss.quorumTotal) * 100)); const isPassed = percent > 50; return (
{/* Animated Background Pulse for Active */} {oss.status === 'active' &&
}

{oss.address}

{oss.status === 'active' ? 'Идет голосование' : oss.status === 'completed' ? 'Завершено' : 'Запланировано'}

{oss.type === 'annual' ? 'Ежегодное отчетное собрание' : (oss.description?.trim() || 'Внеочередное ОСС')}

{(oss.agendaItems?.length ?? 0) > 0 && (

Пунктов повестки: {oss.agendaItems!.length}

)}
до {oss.endDate?.slice(0, 10)}
{/* Quorum Progress */}
Текущий кворум (м²) {percent}% / 50% + 1
{oss.quorumCurrent.toLocaleString()} м² собрано Цель: {oss.quorumTotal.toLocaleString()} м²
{/* Actions List */}
); }) )}

С 2024 года бюллетени ОСС должны быть загружены в ГИС ЖКХ в течение 5 рабочих дней после завершения голосования. Контролируйте сроки передачи оригиналов в ГЖИ.

{/* Modals */} setIsCreateModalOpen(false)} onSuccess={() => fetchOSS()} /> { setIsEditModalOpen(false); setEditingSession(null); }} onSuccess={() => { fetchOSS(); setIsEditModalOpen(false); setEditingSession(null); }} session={editingSession} /> {selectedOSS && ( <> { setIsBallotModalOpen(false); if (!isRegistryModalOpen && !isBulkBallotModalOpen) { setSelectedOSS(null); } }} onSuccess={() => { fetchOSS(); }} ossId={selectedOSS.id} ossAddress={selectedOSS.address} agendaItems={(ossSessions.find(s => s.id === selectedOSS.id) as any)?.agendaItems ?? []} /> { setIsBulkBallotModalOpen(false); if (!isRegistryModalOpen && !isBallotModalOpen) { setSelectedOSS(null); } }} onSuccess={() => { fetchOSS(); }} ossId={selectedOSS.id} ossAddress={selectedOSS.address} agendaItems={(ossSessions.find(s => s.id === selectedOSS.id) as any)?.agendaItems ?? []} /> { setIsRegistryModalOpen(false); if (!isBallotModalOpen && !isBulkBallotModalOpen) { setSelectedOSS(null); } }} ossId={selectedOSS.id} ossAddress={selectedOSS.address} /> )}
); };