import { useState, useEffect, useCallback } from 'react'; import { Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField, FormControl, InputLabel, Select, MenuItem, Alert } from '@mui/material'; import { useEditor, EditorContent } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import Link from '@tiptap/extension-link'; import { TipoAtendimento } from '@prisma/client'; import api from '../../services/api'; // Adicionar esta importação import { XMarkIcon, PaperClipIcon } from '@heroicons/react/24/outline'; // Alterado para v2 import { useDropzone } from 'react-dropzone'; import { uploadFile } from '../../services/upload'; // Implementar debounce manualmente ao invés de usar lodash function debounce(fn: Function, ms = 300) { let timeoutId: ReturnType; return function (this: any, ...args: any[]) { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn.apply(this, args), ms); }; } interface AtendimentoFormProps { open: boolean; onClose: () => void; onSave: (data: any) => Promise; clienteId?: string; processoId?: string; initialData?: { titulo?: string; agendamentoId?: string; tipo?: string; atendimentoInicialId?: string; }; } interface AnexoTemp { id?: string; file: File; nome: string; progresso: number; erro?: string; } export function AtendimentoForm({ open, onClose, onSave, clienteId, processoId, initialData }: AtendimentoFormProps) { const [searchTerm, setSearchTerm] = useState(''); const [searchResults, setSearchResults] = useState([]); const [selectedClient, setSelectedClient] = useState(null); const [atendimentoInicial, setAtendimentoInicial] = useState(null); const [atendimentosIniciais, setAtendimentosIniciais] = useState([]); const [formData, setFormData] = useState({ titulo: '', tipo: TipoAtendimento.INICIAL, conteudo: '', clienteId: clienteId || '', processoId: processoId || '', atendimentoInicialId: '', }); const [anexos, setAnexos] = useState([]); const [uploading, setUploading] = useState(false); // Resetar form ao abrir useEffect(() => { if (open) { setFormData({ titulo: '', tipo: TipoAtendimento.INICIAL, conteudo: '', clienteId: clienteId || '', processoId: processoId || '', atendimentoInicialId: '', }); } }, [open, clienteId, processoId]); useEffect(() => { if (open && initialData) { setFormData(prev => ({ ...prev, titulo: initialData.titulo || prev.titulo, clienteId: clienteId || prev.clienteId, processoId: processoId || prev.processoId, tipo: initialData.tipo || prev.tipo, atendimentoInicialId: initialData.atendimentoInicialId || prev.atendimentoInicialId })); } }, [open, initialData, clienteId, processoId]); const [clientes, setClientes] = useState([]); const [error, setError] = useState(''); const [isSaving, setIsSaving] = useState(false); // Adicionar estado para prevenir múltiplos submits // Buscar lista de clientes useEffect(() => { const fetchClientes = async () => { try { const response = await api.get('/clientes'); setClientes(response.data); } catch (err) { console.error('Erro ao carregar clientes:', err); } }; fetchClientes(); }, []); // Busca de clientes const searchClients = useCallback( debounce(async (term: string) => { if (term.length < 3) return; try { const response = await api.get(`/clientes/search?q=${term}`); setSearchResults(response.data); } catch (error) { console.error('Erro ao buscar clientes:', error); } }, 300), [] ); // Carregar atendimentos iniciais quando selecionar cliente useEffect(() => { if (formData.clienteId && formData.tipo !== TipoAtendimento.INICIAL) { api.get(`/atendimentos/iniciais/${formData.clienteId}`) .then(response => setAtendimentosIniciais(response.data)) .catch(error => console.error('Erro ao carregar atendimentos:', error)); } }, [formData.clienteId, formData.tipo]); const editor = useEditor({ extensions: [ StarterKit, Link.configure({ openOnClick: false, }), ], content: '', onUpdate: ({ editor }) => { setFormData(prev => ({ ...prev, conteudo: editor.getHTML() })); }, }); const onDrop = useCallback((acceptedFiles: File[]) => { const novosAnexos = acceptedFiles.map(file => ({ file, nome: file.name, progresso: 0 })); setAnexos(prev => [...prev, ...novosAnexos]); }, []); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, maxSize: 10485760 // 10MB }); const handleRemoveAnexo = (index: number) => { setAnexos(prev => prev.filter((_, i) => i !== index)); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (isSaving) return; try { setIsSaving(true); setError(''); if (!formData.clienteId) { setError('Selecione um cliente'); setIsSaving(false); return; } // Buscar dados do cliente caso não esteja selecionado if (!selectedClient) { try { const response = await api.get(`/clientes/${formData.clienteId}`); setSelectedClient(response.data); } catch (error) { console.error('Erro ao buscar cliente:', error); setError('Erro ao buscar dados do cliente'); setIsSaving(false); return; } } // Criar atendimento primeiro para ter o protocolo const response = await api.post('/atendimentos', { ...formData, agendamentoId: initialData?.agendamentoId, }); const atendimentoId = response.data.id; const protocolo = response.data.protocolo; // Upload dos anexos com protocolo if (anexos.length > 0) { const anexosPromises = anexos.map(async (anexo) => { try { const clienteNome = selectedClient?.nome || 'cliente'; return await uploadFile( anexo.file, clienteNome, 'atendimento', protocolo ); } catch (error) { console.error('Erro no upload:', error); return null; } }); const anexosUpload = await Promise.all(anexosPromises); const anexosSalvos = anexosUpload.filter(Boolean); if (anexosSalvos.length > 0) { await api.patch(`/atendimentos/${atendimentoId}`, { anexos: anexosSalvos }); } } // Limpar form e fechar modal setFormData({ titulo: '', tipo: TipoAtendimento.INICIAL, conteudo: '', clienteId: '', processoId: '', atendimentoInicialId: '', }); await onSave(response.data); onClose(); } catch (err) { console.error('Erro ao salvar atendimento:', err); setError(err.response?.data?.error || 'Erro ao criar atendimento'); } finally { setIsSaving(false); } }; return (

Novo Atendimento

{/* Busca de Cliente */}
{ setSearchTerm(e.target.value); searchClients(e.target.value); }} className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Buscar cliente..." /> {searchResults.length > 0 && (
{searchResults.map(client => (
{ setSelectedClient(client); setFormData(prev => ({ ...prev, clienteId: client.id })); setSearchTerm(client.nome); setSearchResults([]); }} >
{client.nome}
{client.cpfCnpj}
))}
)}
{/* Tipo de Atendimento */}
{/* Atendimento Inicial (apenas para tipos diferentes de INICIAL) */} {formData.tipo !== TipoAtendimento.INICIAL && (
)} setFormData({ ...formData, titulo: e.target.value })} required /> {/* Editor de rich text */}
{/* Upload de anexos */}

Arraste arquivos aqui ou clique para selecionar

Máximo 10MB por arquivo

{/* Lista de Anexos */} {anexos.length > 0 && (

Arquivos para upload

    {anexos.map((anexo, index) => (
  • {anexo.nome}
    {anexo.erro && ( {anexo.erro} )}
  • ))}
)} {error && (
{error}
)} {/* Botões de ação dentro do form */}
); }