first commit
This commit is contained in:
commit
c9fb67e751
423 files changed
+53287
No files matched your search
@@ -0,0 +1,282 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { api } from '../../lib/api';
|
||||
import { Editor } from '@tinymce/tinymce-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Dialog } from '@headlessui/react';
|
||||
import { DocumentIcon, PencilIcon, TrashIcon, PlusIcon } from '@heroicons/react/24/outline';
|
||||
import { DOCUMENT_VARIABLES } from '../../utils/documentVariables';
|
||||
|
||||
export function GerenciarTemplates() {
|
||||
const [templates, setTemplates] = useState([]);
|
||||
const [editingTemplate, setEditingTemplate] = useState(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isEditorModalOpen, setEditorModalOpen] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
nome: '',
|
||||
tipo: 'OUTROS',
|
||||
descricao: '',
|
||||
conteudo: '',
|
||||
variaveis: []
|
||||
});
|
||||
|
||||
const loadTemplates = async () => {
|
||||
try {
|
||||
const response = await api.get('/documentos-modelos');
|
||||
setTemplates(response.data);
|
||||
} catch (error) {
|
||||
toast.error('Erro ao carregar templates');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadTemplates();
|
||||
}, []);
|
||||
|
||||
const handleSaveTemplate = async (data) => {
|
||||
try {
|
||||
if (editingTemplate) {
|
||||
await api.put(`/documentos-modelos/${editingTemplate.id}`, data);
|
||||
toast.success('Template atualizado com sucesso');
|
||||
} else {
|
||||
await api.post('/documentos-modelos', data);
|
||||
toast.success('Template criado com sucesso');
|
||||
}
|
||||
loadTemplates();
|
||||
setIsModalOpen(false);
|
||||
} catch (error) {
|
||||
toast.error('Erro ao salvar template');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
// Verifica se está editando ou criando novo
|
||||
if (editingTemplate) {
|
||||
await api.put(`/documentos-modelos/${editingTemplate.id}`, {
|
||||
...formData,
|
||||
id: editingTemplate.id // Garantir que o ID seja enviado
|
||||
});
|
||||
toast.success('Template atualizado com sucesso');
|
||||
} else {
|
||||
await api.post('/documentos-modelos', formData);
|
||||
toast.success('Template criado com sucesso');
|
||||
}
|
||||
|
||||
setEditorModalOpen(false);
|
||||
setEditingTemplate(null);
|
||||
await loadTemplates(); // Recarregar a lista após salvar
|
||||
} catch (error) {
|
||||
console.error('Erro ao salvar template:', error);
|
||||
toast.error('Erro ao salvar template');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (templateId: string) => {
|
||||
if (!window.confirm('Tem certeza que deseja excluir este template?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await api.delete(`/documentos-modelos/${templateId}`);
|
||||
toast.success('Template excluído com sucesso');
|
||||
loadTemplates();
|
||||
} catch (error) {
|
||||
console.error('Erro ao excluir template:', error);
|
||||
toast.error('Erro ao excluir template');
|
||||
}
|
||||
};
|
||||
|
||||
const insertVariable = (variable: string) => {
|
||||
const editor = window.tinymce.activeEditor;
|
||||
if (editor) {
|
||||
editor.insertContent(variable);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEdit = (template) => {
|
||||
setEditingTemplate(template); // Guardar o template completo
|
||||
setFormData({
|
||||
...template,
|
||||
id: template.id // Garantir que o ID seja mantido
|
||||
});
|
||||
setEditorModalOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h1 className="text-2xl font-bold">Gerenciar Templates</h1>
|
||||
<button
|
||||
onClick={() => {
|
||||
setFormData({
|
||||
nome: '',
|
||||
tipo: 'OUTROS',
|
||||
descricao: '',
|
||||
conteudo: '',
|
||||
variaveis: []
|
||||
});
|
||||
setEditorModalOpen(true);
|
||||
}}
|
||||
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
||||
>
|
||||
<PlusIcon className="w-5 h-5 mr-2" />
|
||||
Novo Template
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{templates.map((template) => (
|
||||
<div key={template.id} className="bg-white dark:bg-gray-800 rounded-lg shadow p-4">
|
||||
<div className="flex items-center mb-3">
|
||||
<DocumentIcon className="w-6 h-6 text-gray-500 mr-2" />
|
||||
<h3 className="font-semibold">{template.nome}</h3>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">{template.descricao}</p>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => handleEdit(template)}
|
||||
className="inline-flex items-center px-3 py-1.5 text-sm text-blue-600 border border-blue-200 rounded-md hover:bg-blue-50"
|
||||
>
|
||||
<PencilIcon className="w-4 h-4 mr-1" />
|
||||
Editar
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(template.id)}
|
||||
className="inline-flex items-center px-3 py-1.5 text-sm text-red-600 border border-red-200 rounded-md hover:bg-red-50"
|
||||
>
|
||||
<TrashIcon className="w-4 h-4 mr-1" />
|
||||
Excluir
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Modal de Edição */}
|
||||
<Dialog open={isEditorModalOpen} onClose={() => setEditorModalOpen(false)} className="relative z-50">
|
||||
<div className="fixed inset-0 bg-black/30" aria-hidden="true" />
|
||||
<div className="fixed inset-0 flex items-center justify-center p-4 overflow-y-auto">
|
||||
<Dialog.Panel className="w-full max-w-4xl bg-white rounded-lg shadow-xl my-4">
|
||||
<div className="max-h-[90vh] overflow-y-auto">
|
||||
<form onSubmit={handleSubmit} className="p-6">
|
||||
<h2 className="text-xl font-bold mb-4">
|
||||
{editingTemplate ? 'Editar Template' : 'Novo Template'}
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block mb-1">Nome</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.nome}
|
||||
onChange={(e) => setFormData({...formData, nome: e.target.value})}
|
||||
className="w-full p-2 border rounded"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-1">Tipo</label>
|
||||
<select
|
||||
value={formData.tipo}
|
||||
onChange={(e) => setFormData({...formData, tipo: e.target.value})}
|
||||
className="w-full p-2 border rounded"
|
||||
>
|
||||
<option value="PETICAO">Petição</option>
|
||||
<option value="CONTRATO">Contrato</option>
|
||||
<option value="PROCURACAO">Procuração</option>
|
||||
<option value="DECLARACAO">Declaração</option>
|
||||
<option value="OUTROS">Outros</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-1">Descrição</label>
|
||||
<textarea
|
||||
value={formData.descricao}
|
||||
onChange={(e) => setFormData({...formData, descricao: e.target.value})}
|
||||
className="w-full p-2 border rounded"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label className="block mb-1">Variáveis Disponíveis</label>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 p-2 border rounded bg-gray-50">
|
||||
{Object.entries(DOCUMENT_VARIABLES).map(([category, variables]) => (
|
||||
<div key={category} className="space-y-1">
|
||||
<h4 className="font-bold text-sm text-gray-700">{category}</h4>
|
||||
{Object.entries(variables).map(([name, variable]) => (
|
||||
<button
|
||||
key={name}
|
||||
type="button"
|
||||
onClick={() => insertVariable(variable)}
|
||||
className="text-xs text-blue-600 hover:text-blue-800 block"
|
||||
>
|
||||
{name.replace(/_/g, ' ')}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-1">Conteúdo</label>
|
||||
<Editor
|
||||
apiKey={import.meta.env.VITE_TINYMCE_API_KEY}
|
||||
value={formData.conteudo}
|
||||
onEditorChange={(content) => setFormData({...formData, conteudo: content})}
|
||||
init={{
|
||||
height: 400,
|
||||
menubar: true,
|
||||
plugins: [
|
||||
'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
|
||||
'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
|
||||
'insertdatetime', 'media', 'table', 'help', 'wordcount'
|
||||
],
|
||||
toolbar: 'variables | undo redo | formatselect | bold italic | alignleft aligncenter alignright',
|
||||
setup: (editor) => {
|
||||
editor.ui.registry.addMenuButton('variables', {
|
||||
text: 'Variáveis',
|
||||
fetch: (callback) => {
|
||||
const items = Object.entries(DOCUMENT_VARIABLES).flatMap(([category, vars]) =>
|
||||
Object.entries(vars).map(([name, value]) => ({
|
||||
type: 'menuitem',
|
||||
text: `${category} - ${name.replace(/_/g, ' ')}`,
|
||||
onAction: () => editor.insertContent(value)
|
||||
}))
|
||||
);
|
||||
callback(items);
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 mt-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setEditorModalOpen(false)}
|
||||
className="px-4 py-2 text-gray-600 border rounded-md hover:bg-gray-50"
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
||||
>
|
||||
Salvar
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user