import React, { useEffect, useState, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import api from '../services/api'; import { Button } from '../components/Button'; import { Plus, Search, Pencil, Trash2, Activity, Box, CheckCircle2, History, Printer } from 'lucide-react'; import { format } from 'date-fns'; import { useAuth } from '../context/useAuth'; import { useToast } from '../hooks/useToast'; import { MobileList } from '../components/MobileList'; import { CreateProjectModal } from '../components/modals/CreateProjectModal'; import { useOrganization } from '@clerk/clerk-react'; import { useSystemSettings } from '../context/SystemSettingsContext'; import { Modal } from '../components/Modal'; import { ConfirmModal } from '../components/ConfirmModal'; import { ColorBubble } from '../components/ColorBubble'; import type { Project, Inspection } from '../types'; import { AnalyticalReport } from '../components/reports/AnalyticalReport'; import { GeneralProjectReport } from '../components/reports/GeneralProjectReport'; export const ProjectList: React.FC = () => { const [projects, setProjects] = useState([]); const [inspections, setInspections] = useState([]); const [loading, setLoading] = useState(true); const [isModalOpen, setIsModalOpen] = useState(false); const [selectedProject, setSelectedProject] = useState(undefined); const [confirmModal, setConfirmModal] = useState<{ isOpen: boolean; title: string; description: string; type: 'danger' | 'warning' | 'info'; onConfirm: () => void; }>({ isOpen: false, title: '', description: '', type: 'info', onConfirm: () => { } }); const [statsModal, setStatsModal] = useState<{ isOpen: boolean; title: string; type: 'alerts' | 'inspections' | null }>({ isOpen: false, title: '', type: null }); const [searchTerm, setSearchTerm] = useState(''); const [viewStatus, setViewStatus] = useState<'active' | 'archived'>('active'); const [printingProject, setPrintingProject] = useState(null); const [isPrinting, setIsPrinting] = useState(false); const [isPrintingGeneral, setIsPrintingGeneral] = useState(false); const navigate = useNavigate(); const { appUser } = useAuth(); const { showToast } = useToast(); const { organization } = useOrganization(); const { settings } = useSystemSettings(); const logoUrl = settings?.appLogoUrl || organization?.imageUrl; const isAdmin = appUser?.email === 'admtracksteel@gmail.com' || appUser?.role === 'admin'; const fetchProjects = useCallback(async () => { try { setLoading(true); const [projRes, inspRes] = await Promise.all([ api.get(`/projects?status=${viewStatus}`), api.get('/inspections') ]); setProjects(projRes.data); setInspections(inspRes.data); } catch (error) { console.error('Error fetching data:', error); showToast('Não foi possível carregar os dados.', 'error'); } finally { setLoading(false); } }, [viewStatus, showToast]); useEffect(() => { fetchProjects(); }, [fetchProjects]); const handleEdit = (project: Project) => { if (viewStatus === 'archived') return; setSelectedProject(project); setIsModalOpen(true); }; const handleDelete = async (id: string) => { const project = projects.find(p => p.id === id); setConfirmModal({ isOpen: true, title: 'Excluir Projeto', description: `Tem certeza que deseja excluir o projeto "${project?.name}"? Todos os dados vinculados serão perdidos permanentemente.`, type: 'danger', onConfirm: async () => { try { await api.delete(`/projects/${id}`); fetchProjects(); showToast('O projeto foi removido com sucesso.', 'success'); } catch (error) { console.error('Error deleting project:', error); showToast('Não foi possível excluir o projeto.', 'error'); } setConfirmModal(prev => ({ ...prev, isOpen: false })); } }); }; const handlePrint = async (projectId: string) => { try { setIsPrinting(true); const response = await api.get(`/projects/${projectId}`); setPrintingProject(response.data); // Wait for state to update and layout to render setTimeout(() => { window.print(); setIsPrinting(false); setPrintingProject(null); }, 500); } catch (error) { console.error('Error fetching project for print:', error); showToast('Erro ao gerar relatório.', 'error'); setIsPrinting(false); } }; const handlePrintGeneral = () => { setIsPrintingGeneral(true); setTimeout(() => { window.print(); setIsPrintingGeneral(false); }, 500); }; const handleArchive = async (project: Project) => { const isArchiving = viewStatus === 'active'; setConfirmModal({ isOpen: true, title: isArchiving ? 'Concluir Obras' : 'Reativar Projeto', description: `Deseja ${isArchiving ? 'arquivar' : 'reativar'} o projeto "${project.name}"?`, type: 'info', onConfirm: async () => { try { await api.patch(`/projects/${project.id}/archive`); showToast(`O projeto ${project.name} foi movido com sucesso.`, 'success'); fetchProjects(); } catch (error) { console.error('Error archiving project:', error); showToast('Não foi possível alterar o status do projeto.', 'error'); } setConfirmModal(prev => ({ ...prev, isOpen: false })); } }); }; const filteredProjects = projects.filter(p => p.name.toLowerCase().includes(searchTerm.toLowerCase()) || p.client.toLowerCase().includes(searchTerm.toLowerCase()) ); const columns = [ { header: 'Obra / Projeto', accessor: (item: Project) => (
navigate(`/project/${item.id}`)} > {item.name} {item.client} Gestor: {item.technician || 'Não informado'}
), className: 'min-w-[150px]' }, { header: 'Evolução', accessor: (item: Project) => { const projectInspections = inspections.filter(i => i.projectId === item.id); const sumWeight = projectInspections.reduce((acc, curr) => acc + (curr.weightKg || 0), 0); const totalWeight = item.weightKg || 0; const percentage = totalWeight > 0 ? Math.min(Math.round((sumWeight / totalWeight) * 100), 100) : 0; return (
{percentage}%
); }, className: 'hidden sm:table-cell w-[80px]' }, { header: 'Cronograma', accessor: (item: Project) => (
Início: {item.startDate ? format(new Date(item.startDate), 'dd/MM/yy') : '--/--/--'}
Término: {item.endDate ? format(new Date(item.endDate), 'dd/MM/yy') : '--/--/--'}
), className: 'hidden md:table-cell w-[110px]' }, { header: 'Peso(kgf)', accessor: (item: Project) => (
{item.weightKg ? item.weightKg.toLocaleString('pt-BR') : '0'} Est. Total
), className: 'w-[90px]' }, { header: 'Tinta', accessor: (item: Project) => { const schemes = item.paintingSchemes || []; if (schemes.length === 0) return ---; return (
{schemes.slice(0, 2).map((scheme, idx) => (
{scheme.name} {scheme.coat || 'Demão'}
))} {schemes.length > 2 && ( + {schemes.length - 2} demãos )}
); }, className: 'hidden xl:table-cell w-[130px]' }, { header: 'Cor', accessor: (item: Project) => { const schemes = item.paintingSchemes || []; if (schemes.length === 0) return
; return (
{schemes.slice(0, 2).map((scheme, idx) => (
{scheme.color || '-'}
))}
); }, className: 'hidden xl:table-cell w-[80px]' }, { header: 'Ações', accessor: (item: Project) => (
{isAdmin && ( )} {isAdmin && viewStatus === 'active' && ( <> )}
) } ]; if (loading) { return (
); } return (
{/* Page Header */}

{viewStatus === 'active' ? 'Gestão de Obras' : 'Obras Arquivadas'}

{viewStatus === 'active' ? 'Monitoramento e controle de esquemas industriais' : 'Histórico de projetos concluídos e arquivados'}

setSearchTerm(e.target.value)} />
{isAdmin && viewStatus === 'active' && ( )}
{/* Quick Stats */}
{[ { label: viewStatus === 'active' ? 'Projetos Ativos' : 'Projetos Arquivados', value: projects.length, color: viewStatus === 'active' ? 'text-primary' : 'text-indigo-500', bg: 'bg-primary/5', onClick: () => { }, interactive: false }, { label: 'Inspeções Realizadas', value: inspections.length, color: 'text-success', bg: 'bg-success/5', onClick: () => setStatsModal({ isOpen: true, title: 'Resumo de Inspeções', type: 'inspections' }), interactive: true }, { label: 'Conformidade', value: inspections.length > 0 ? `${((inspections.filter(i => i.appearance !== 'rejected').length / inspections.length) * 100).toFixed(1)}%` : '100%', color: 'text-blue-500', bg: 'bg-blue-500/5', onClick: () => { }, interactive: false }, { label: 'Alertas Pendentes', value: inspections.filter(i => i.appearance === 'rejected' || i.defects).length, color: 'text-error', bg: 'bg-error/5', onClick: () => setStatsModal({ isOpen: true, title: 'Resumo de Alertas', type: 'alerts' }), interactive: true } ].map((stat, i) => (
{stat.label}
{stat.value} TOTAL
))}
{/* Project Table/List */}
item.id} onItemClick={(item) => navigate(`/project/${item.id}`)} titleAccessor="name" subtitleAccessor="client" /> {projects.length === 0 && (
{viewStatus === 'active' ? : }

{viewStatus === 'active' ? 'Nenhuma obra encontrada' : 'Nenhuma obra arquivada'}

{viewStatus === 'active' ? 'Inicie o monitoramento criando seu primeiro projeto.' : 'Ainda não existem projetos concluídos no histórico.'}

)}
{ setIsModalOpen(false); setSelectedProject(undefined); }} onSuccess={fetchProjects} initialData={selectedProject} /> setStatsModal({ ...statsModal, isOpen: false })} title={statsModal.title} >
{statsModal.type === 'alerts' && ( inspections.filter(i => i.appearance === 'rejected' || i.defects).map(insp => { const project = projects.find(p => p.id === insp.projectId); return (
navigate(`/project/${insp.projectId}`, { state: { activeTab: 'inspection' } })} >
{project?.name || 'Projeto Desconhecido'} CRÍTICO

"{insp.defects || 'Defeito não especificado'}"

Inspetor: {insp.inspector || '--'} Data: {insp.date ? format(new Date(insp.date), 'dd/MM/yy') : '--'}
); }) )} {statsModal.type === 'inspections' && ( inspections.slice(0, 10).map(insp => { const project = projects.find(p => p.id === insp.projectId); return (
navigate(`/project/${insp.projectId}`, { state: { activeTab: 'inspection' } })} >
{project?.name || '---'} {insp.pieceDescription || 'Geral'}
{insp.appearance === 'rejected' ? 'REJEITADO' : 'APROVADO'} {insp.date ? format(new Date(insp.date), 'dd/MM/yy') : '--'}
); }) )}
setConfirmModal(prev => ({ ...prev, isOpen: false }))} onConfirm={confirmModal.onConfirm} title={confirmModal.title} description={confirmModal.description} type={confirmModal.type} /> {printingProject && ( )} {isPrintingGeneral && ( )}
); };