import React, { useState, useEffect } from 'react'; import { Employee, EmployeeCategory } from '../types'; import { Plus, Edit2, Trash2, X, Search, Users, Briefcase, Calendar, Phone, Mail, FileText, Settings2 } from 'lucide-react'; import { useDialog } from '../DialogContext'; const Employees: React.FC = () => { const { showAlert, showConfirm } = useDialog(); const [searchTerm, setSearchTerm] = useState(''); const [isModalOpen, setIsModalOpen] = useState(false); const [isCategoryModalOpen, setIsCategoryModalOpen] = useState(false); const [isClosing, setIsClosing] = useState(false); const [editingEmployee, setEditingEmployee] = useState(null); const [editingCategory, setEditingCategory] = useState(null); const [formData, setFormData] = useState>({ name: '', cpf: '', phone: '', email: '', admissionDate: new Date().toISOString().split('T')[0], categoryId: '' }); const [categoryFormData, setCategoryFormData] = useState({ name: '' }); const [employees, setEmployees] = useState([]); const [categories, setCategories] = useState([]); const [isLoadingData, setIsLoadingData] = useState(true); const loadData = async () => { try { setIsLoadingData(true); const [empRes, catRes] = await Promise.all([ fetch(`/api/funcionarios?t=${Date.now()}`), fetch(`/api/categorias_funcionarios?t=${Date.now()}`) ]); const empData = await empRes.json(); const catData = await catRes.json(); // Mapeamento caso a API retorne os nomes das colunas diferentes do TS const mappedEmployees = (empData.funcionarios || []).map((e: any) => ({ id: e.id, name: e.name || e.nome, cpf: e.cpf, email: e.email, phone: e.phone || e.telefone, admissionDate: e.hireDate || (e.data_admissao ? e.data_admissao.substring(0, 10) : ''), categoryId: e.categoryId || e.categoria_id })); const mappedCategories = (catData.categorias || []).map((c: any) => ({ id: c.id, name: c.name || c.nome })); setEmployees(mappedEmployees); setCategories(mappedCategories); } catch (err) { console.error('Erro ao buscar funcionários/categorias:', err); showAlert('Erro', 'Não foi possível carregar a lista do servidor.', 'error'); } finally { setIsLoadingData(false); } }; useEffect(() => { loadData(); }, []); const filteredEmployees = employees.filter(emp => (emp.name || '').toLowerCase().includes((searchTerm || '').toLowerCase()) || (emp.cpf || '').includes(searchTerm || '') || (emp.email || '').toLowerCase().includes((searchTerm || '').toLowerCase()) ); const closeModal = () => { setIsClosing(true); setTimeout(() => { setIsModalOpen(false); setIsClosing(false); setEditingEmployee(null); setFormData({ name: '', cpf: '', phone: '', email: '', admissionDate: new Date().toISOString().split('T')[0], categoryId: '' }); }, 400); }; const closeCategoryModal = () => { setIsCategoryModalOpen(false); setEditingCategory(null); setCategoryFormData({ name: '' }); }; const handleEdit = (emp: Employee) => { setEditingEmployee(emp); setFormData({ name: emp.name, cpf: emp.cpf, phone: emp.phone, email: emp.email, admissionDate: emp.admissionDate, categoryId: emp.categoryId }); setIsModalOpen(true); }; const handleDelete = (emp: Employee) => { showConfirm( 'Remover Funcionário', `Tem certeza que deseja remover ${emp.name}?`, async () => { try { const res = await fetch(`/api/funcionarios/${emp.id}`, { method: 'DELETE' }); if (!res.ok) throw new Error('Falha ao excluir'); await loadData(); showAlert('Sucesso', 'Funcionário removido com sucesso.', 'success'); } catch (error) { showAlert('Erro', 'Ocorreu um erro ao excluir o funcionário.', 'error'); } } ); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!formData.categoryId) { showAlert('Atenção', 'Selecione uma categoria para o funcionário.', 'warning'); return; } const payload = { nome: formData.name, cpf: formData.cpf, email: formData.email, telefone: formData.phone, data_admissao: formData.admissionDate, categoria_id: formData.categoryId }; try { if (editingEmployee) { const res = await fetch(`/api/funcionarios/${editingEmployee.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!res.ok) throw new Error('Failed to update'); showAlert('Sucesso', 'Funcionário atualizado com sucesso.', 'success'); } else { const res = await fetch('/api/funcionarios', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...payload, id: crypto.randomUUID() }) }); if (!res.ok) throw new Error('Failed to create'); showAlert('Sucesso', 'Funcionário cadastrado com sucesso.', 'success'); } await loadData(); closeModal(); } catch (err) { showAlert('Erro', 'Ocorreu um problema de comunicação com a API.', 'error'); } }; const handleCategorySubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!categoryFormData.name.trim()) return; try { if (editingCategory) { const res = await fetch(`/api/categorias_funcionarios/${editingCategory.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ nome: categoryFormData.name }) }); if (!res.ok) throw new Error('Failed to update'); } else { const res = await fetch('/api/categorias_funcionarios', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: crypto.randomUUID(), nome: categoryFormData.name }) }); if (!res.ok) throw new Error('Failed to create'); } await loadData(); setCategoryFormData({ name: '' }); setEditingCategory(null); } catch (err) { showAlert('Erro', 'Ocorreu um erro ao salvar categoria.', 'error'); } }; const handleDeleteCategory = (cat: EmployeeCategory) => { const hasEmployees = employees.some(emp => emp.categoryId === cat.id); if (hasEmployees) { showAlert('Atenção', 'Não é possível excluir uma categoria que possui funcionários vinculados.', 'warning'); return; } showConfirm( 'Remover Categoria', `Deseja remover a categoria "${cat.name}"?`, async () => { try { const res = await fetch(`/api/categorias_funcionarios/${cat.id}`, { method: 'DELETE' }); if (!res.ok) throw new Error('Failed to delete'); await loadData(); } catch (err) { showAlert('Erro', 'Ocorreu um erro ao excluir a categoria.', 'error'); } } ); }; const inputClass = "w-full px-4 py-3 bg-white text-black border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-all shadow-sm"; return (
{/* Header */}

Funcionários PostgreSQL

Gerencie sua equipe e categorias profissionais.

{/* Search and Stats */}
setSearchTerm(e.target.value)} />

Total Equipe

{employees.length}

{/* Employees Grid */}
{filteredEmployees.map(emp => { const category = categories.find(c => c.id === emp.categoryId); return (

{emp.name}

{category?.name || 'Sem Categoria'}
CPF: {emp.cpf}
{emp.phone}
{emp.email}
Admissão: {new Date(emp.admissionDate).toLocaleDateString('pt-BR')}
); })}
{!isLoadingData && employees.length === 0 && (

Nenhum funcionário cadastrado

Comece adicionando os membros da sua equipe.

)} {/* Employee Modal */} {isModalOpen && (

{editingEmployee ? 'Editar Funcionário' : 'Novo Funcionário'}

Preencha os dados profissionais.

setFormData({ ...formData, name: e.target.value })} />
{ const val = e.target.value.replace(/\D/g, '').replace(/(\d{3})(\d)/, '$1.$2').replace(/(\d{3})(\d)/, '$1.$2').replace(/(\d{3})(\d{1,2})/, '$1-$2').slice(0, 14); setFormData({ ...formData, cpf: val }); }} />
{ const val = e.target.value.replace(/\D/g, '').replace(/(\d{2})(\d)/, '($1) $2').replace(/(\d{5})(\d)/, '$1-$2').slice(0, 15); setFormData({ ...formData, phone: val }); }} />
setFormData({ ...formData, email: e.target.value })} />
setFormData({ ...formData, admissionDate: e.target.value })} />
)} {/* Categories Modal */} {isCategoryModalOpen && (

Gerenciar Categorias

setCategoryFormData({ name: e.target.value })} />
{categories.map(cat => (
{cat.name}
))} {categories.length === 0 && (

Nenhuma categoria cadastrada.

)}
)}
); }; export default Employees;