From 48ea0c7303a65401ffea84b5d878cf4b5578f713 Mon Sep 17 00:00:00 2001 From: "Rodrigo B. de Sousa Martins" Date: Sun, 27 Jul 2025 18:57:22 -0300 Subject: [PATCH] undo commit --- src/components/DocumentosPage.tsx | 3 +- src/components/documentos/DocumentoEditor.tsx | 26 +- src/components/documentos/DocumentosPage.tsx | 246 +++++++++++++++++- src/components/tarefas/AnexosList.tsx | 16 +- src/components/tarefas/TarefasList.tsx | 6 +- src/server/app.ts | 42 +++ 6 files changed, 313 insertions(+), 26 deletions(-) create mode 100644 src/server/app.ts diff --git a/src/components/DocumentosPage.tsx b/src/components/DocumentosPage.tsx index 5d3f58f..a7d95d6 100644 --- a/src/components/DocumentosPage.tsx +++ b/src/components/DocumentosPage.tsx @@ -1,11 +1,12 @@ import { useState, useEffect, useCallback } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; +import { useParams, useNavigate, Link } from 'react-router-dom'; import { DocumentIcon, PlusIcon, TrashIcon, EyeIcon, XMarkIcon, DocumentArrowDownIcon } from '@heroicons/react/24/outline'; import { DocumentoEditor } from './documentos/DocumentoEditor'; import { api } from '../lib/api'; import { toast } from 'react-toastify'; import { useAuth } from '../contexts/AuthContext'; import { Dialog } from '@headlessui/react'; +import { DocumentoViewer } from './documentos/DocumentoViewer'; interface Documento { id: string; diff --git a/src/components/documentos/DocumentoEditor.tsx b/src/components/documentos/DocumentoEditor.tsx index 4001b2c..2316a9c 100644 --- a/src/components/documentos/DocumentoEditor.tsx +++ b/src/components/documentos/DocumentoEditor.tsx @@ -3,8 +3,9 @@ import { Editor } from '@tinymce/tinymce-react'; import { Button, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'; import { Save as SaveIcon } from '@mui/icons-material'; import { XMarkIcon } from '@heroicons/react/24/outline'; -import { toast } from 'react-toastify'; -import { api } from '../../lib/api'; +import { toast } from 'react-toastify'; // Adicionar esta importação +import { api } from '../../lib/api'; // Adicionar esta importação +import { DocumentoService } from '../../services/DocumentoService'; // Adicionar esta importação interface DocumentoEditorProps { isOpen: boolean; @@ -23,6 +24,15 @@ interface TemplateVariables { [key: string]: any; } +const preencherTemplate = (template: string, variables: TemplateVariables) => { + let result = template; + Object.entries(variables).forEach(([key, value]) => { + const regex = new RegExp(`{{${key}}}`, 'g'); + result = result.replace(regex, value); + }); + return result; +}; + export function DocumentoEditor({ isOpen, onClose, @@ -276,7 +286,7 @@ export function DocumentoEditor({ - - - - ); -} diff --git a/src/components/documentos/DocumentosPage.tsx b/src/components/documentos/DocumentosPage.tsx index 29fa00b..2d9c685 100644 --- a/src/components/documentos/DocumentosPage.tsx +++ b/src/components/documentos/DocumentosPage.tsx @@ -1 +1,245 @@ -// Arquivo duplicado. Pode ser removido com segurança. \ No newline at end of file +import { useState, useEffect, useCallback } from 'react'; +import { useParams, useNavigate, Link } from 'react-router-dom'; +import { DocumentIcon, PlusIcon, TrashIcon, EyeIcon, CogIcon } from '@heroicons/react/24/outline'; +import { DocumentoEditor } from './DocumentoEditor'; +import { api } from '../lib/api'; +import { toast } from 'react-toastify'; +import { useAuth } from '../contexts/AuthContext'; +import { Dialog } from '@headlessui/react'; +import { XMarkIcon } from '@heroicons/react/24/outline'; +import { DocumentoViewer } from './DocumentoViewer'; + +export function DocumentosPage() { + const [isEditorOpen, setIsEditorOpen] = useState(false); + const [documentos, setDocumentos] = useState([]); + const [modelos, setModelos] = useState([]); + const [tabValue, setTabValue] = useState(0); + const [isLoading, setIsLoading] = useState(true); + const { processoId, atendimentoId } = useParams(); + const navigate = useNavigate(); + const { user, signOut } = useAuth(); + const [documentoVisualizado, setDocumentoVisualizado] = useState(null); + const [isVisualizadorOpen, setVisualizadorOpen] = useState(false); + + const loadData = useCallback(async () => { + if (!user) { + navigate('/login'); + return; + } + + try { + setIsLoading(true); + await Promise.all([loadDocumentos(), loadModelos()]); + } catch (error) { + console.error('Erro ao carregar dados:', error); + if (error?.response?.status === 401) { + signOut(); // Usar a função do AuthContext + navigate('/login'); + } else { + toast.error('Erro ao carregar dados'); + } + } finally { + setIsLoading(false); + } + }, [user, navigate, signOut]); // Adicionar signOut nas dependências + + useEffect(() => { + loadData(); + }, [loadData, processoId, atendimentoId]); + + const loadDocumentos = async () => { + const params = {}; + if (processoId) params.processoId = processoId; + if (atendimentoId) params.atendimentoId = atendimentoId; + + const response = await api.get('/documentos', { params }); + setDocumentos(response.data); + }; + + const loadModelos = async () => { + const response = await api.get('/documentos-modelos'); + setModelos(response.data); + }; + + const handleSave = async (data) => { + try { + if (processoId) data.processoId = processoId; + if (atendimentoId) data.atendimentoId = atendimentoId; + + await api.post('/documentos', data); + toast.success('Documento salvo com sucesso'); + await loadDocumentos(); + setIsEditorOpen(false); + } catch (error) { + console.error('Erro ao salvar documento:', error); + if (error?.response?.status === 401) { + toast.error('Sessão expirada. Por favor, faça login novamente.'); + } else { + toast.error('Erro ao salvar documento'); + } + } + }; + + const handleDeleteDocumento = async (id) => { + try { + await api.delete(`/documentos/${id}`); + toast.success('Documento excluído com sucesso'); + await loadDocumentos(); + } catch (error) { + console.error('Erro ao excluir documento:', error); + if (error?.response?.status === 401) { + toast.error('Sessão expirada. Por favor, faça login novamente.'); + } else { + toast.error('Erro ao excluir documento'); + } + } + }; + + const handleVisualizar = async (documento) => { + try { + const configRes = await api.get('/documentos/configuracoes'); + + setDocumentoVisualizado({ + ...documento, + escritorio: configRes.data + }); + setVisualizadorOpen(true); + } catch (error) { + console.error('Erro ao visualizar documento:', error); + toast.error('Erro ao visualizar documento'); + } + }; + + if (isLoading) { + return
Carregando...
; + } + + return ( +
+
+

Documentos

+
+ + {user?.perfil === 'ADVOGADO_ADMIN' && ( + <> + + + + )} +
+
+ +
+ +
+ + {tabValue === 0 && ( +
+ {documentos.map((doc) => ( +
+
+
+ +

{doc.nome}

+
+

+ Criado em: {new Date(doc.criadoEm).toLocaleDateString()} +

+
+ + +
+
+
+ ))} +
+ )} + + {tabValue === 1 && ( +
+ {modelos.map((modelo) => ( +
+
+
+ +

{modelo.nome}

+
+

{modelo.descricao}

+ +
+
+ ))} +
+ )} + + setIsEditorOpen(false)} + onSave={handleSave} + processoId={processoId} + atendimentoId={atendimentoId} + /> + + setVisualizadorOpen(false)} + /> +
+ ); +} \ No newline at end of file diff --git a/src/components/tarefas/AnexosList.tsx b/src/components/tarefas/AnexosList.tsx index aa1021b..2c032e2 100644 --- a/src/components/tarefas/AnexosList.tsx +++ b/src/components/tarefas/AnexosList.tsx @@ -28,6 +28,14 @@ export const AnexosList: React.FC = ({ anexos, onChange }) => { const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }); + const formatFileSize = (bytes: number) => { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + return (
= ({ anexos, onChange }) => {
); -}; - -
- - ))} - - - ); }; diff --git a/src/components/tarefas/TarefasList.tsx b/src/components/tarefas/TarefasList.tsx index cc5f3db..80db77f 100644 --- a/src/components/tarefas/TarefasList.tsx +++ b/src/components/tarefas/TarefasList.tsx @@ -1,8 +1,10 @@ import React from 'react'; +import { useQuery } from 'react-query'; import { Tarefa } from '../../types/entities'; import { format } from 'date-fns'; import { ptBR } from 'date-fns/locale'; import { PencilIcon, TrashIcon } from '@heroicons/react/24/outline'; +import api from '../../services/api'; import { useAuth } from '../../contexts/AuthContext'; import { useTarefas } from '../../hooks/useTarefas'; @@ -13,7 +15,7 @@ interface TarefasListProps { export const TarefasList: React.FC = ({ onEdit, onStatusChange }) => { const { user } = useAuth(); - const { tarefas, deleteTarefa } = useTarefas(); + const { tarefas, isLoading, deleteTarefa } = useTarefas(); const getPrioridadeColor = (prioridade: string) => { const colors = { @@ -135,6 +137,4 @@ export const TarefasList: React.FC = ({ onEdit, onStatusChange ); -}; - ); }; diff --git a/src/server/app.ts b/src/server/app.ts new file mode 100644 index 0000000..4664538 --- /dev/null +++ b/src/server/app.ts @@ -0,0 +1,42 @@ +import express from 'express'; +import bodyParser from 'body-parser'; +import cors from 'cors'; +import dotenv from 'dotenv'; +import path from 'path'; +import { PrismaClient } from '@prisma/client'; +import provasRoutes from './routes/provas'; +import calculosJudiciaisRoutes from './routes/calculosJudiciais'; +import indicesMonetariosRoutes from './routes/indicesMonetarios'; +import pensaoAlimenticiaRoutes from './routes/pensaoAlimenticia'; +import financeiroPainelRoutes from './routes/financeiroPainel'; + +dotenv.config(); + +const app = express(); +const prisma = new PrismaClient(); + +// Configurar para confiar em proxies, permitindo a leitura de X-Forwarded-For +// Isso é necessário se sua aplicação estiver atrás de um balanceador de carga, +// proxy reverso ou Cloudflare +app.set('trust proxy', true); + +app.use(cors()); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); + +app.use('/api/provas', provasRoutes); +app.use('/api/calculos', calculosJudiciaisRoutes); +app.use('/api/indices', indicesMonetariosRoutes); +app.use('/api/pensao-alimenticia', pensaoAlimenticiaRoutes); +app.use('/api/financeiro-painel', financeiroPainelRoutes); + +app.use(express.static(path.join(__dirname, 'public'))); +app.set('trust proxy', true); // Importante para obter o IP real em ambientes com proxy + +const PORT = process.env.PORT || 5000; + +app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); +}); + +export { app, prisma }; \ No newline at end of file