first commit

This commit is contained in:
rodrigo committed 2025-04-10 23:07:21 -03:00
commit c9fb67e751
423 files changed
+53287

No files matched your search

@@ -0,0 +1,344 @@
import { useState, useEffect } from 'react';
import { api } from '../../lib/api';
import { toast } from 'react-toastify';
import { Dialog } from '@headlessui/react';
import { DocumentArrowUpIcon } from '@heroicons/react/24/outline';
import { Editor } from '@tinymce/tinymce-react';
import { DOCUMENT_VARIABLES } from '../../utils/documentVariables';
// Definir interface para o estado
interface ConfigState {
timbradoUrl: string;
timbradoMarginTop: number;
timbradoMarginBottom: number;
timbradoMarginLeft: number;
timbradoMarginRight: number;
templatePadrao: string;
margensTemplate: {
top: number;
bottom: number;
left: number;
right: number;
};
}
export function ConfiguracaoDocumento() {
const [config, setConfig] = useState<ConfigState>({
timbradoUrl: '',
timbradoMarginTop: 100,
timbradoMarginBottom: 50,
timbradoMarginLeft: 50,
timbradoMarginRight: 50,
templatePadrao: '',
margensTemplate: {
top: 35,
bottom: 29,
left: 25,
right: 25
}
});
const [isPreviewOpen, setPreviewOpen] = useState(false);
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
loadConfig();
}, []);
const loadConfig = async () => {
try {
const response = await api.get('/documentos/configuracoes');
console.log('Configurações carregadas:', response.data); // Debug
if (response.data) {
setConfig({
timbradoUrl: response.data.timbradoUrl || '',
timbradoMarginTop: response.data.timbradoMarginTop || 100,
timbradoMarginBottom: response.data.timbradoMarginBottom || 50,
timbradoMarginLeft: response.data.timbradoMarginLeft || 50,
timbradoMarginRight: response.data.timbradoMarginRight || 50,
templatePadrao: response.data.templatePadrao || '',
margensTemplate: {
top: response.data.margensTemplate?.top || 35,
bottom: response.data.margensTemplate?.bottom || 29,
left: response.data.margensTemplate?.left || 25,
right: response.data.margensTemplate?.right || 25
}
});
}
} catch (error) {
console.error('Erro ao carregar configurações:', error);
toast.error('Erro ao carregar configurações');
}
};
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
// Atualizado para aceitar PDF
if (!['image/png', 'image/svg+xml', 'application/pdf'].includes(file.type)) {
toast.error('Por favor, use apenas arquivos PDF, PNG ou SVG');
return;
}
if (file.size > 5 * 1024 * 1024) { // Aumentado para 5MB para acomodar PDFs
toast.error('O arquivo deve ter no máximo 5MB');
return;
}
try {
const formData = new FormData();
formData.append('timbrado', file);
const response = await api.post('/documentos/upload-timbrado', formData);
setConfig(prev => ({ ...prev, timbradoUrl: response.data.url }));
toast.success('Timbrado atualizado com sucesso');
} catch (error) {
toast.error('Erro ao fazer upload do timbrado');
}
};
const handleSave = async () => {
try {
setIsSaving(true);
await api.post('/documentos/configuracoes', config);
toast.success('Configurações salvas com sucesso');
} catch (error) {
toast.error('Erro ao salvar configurações');
} finally {
setIsSaving(false);
}
};
const handleEditorSetup = (editor: any) => {
editor.ui.registry.addMenuButton('variables', {
text: 'Variáveis',
fetch: (callback: Function) => {
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 as string)
}))
);
callback(items);
}
});
};
// Modificar a configuração do TinyMCE para remover o plugin 'template'
const editorConfig = {
height: 400,
menubar: true,
plugins: [
'code', 'preview' // Removido 'template'
],
toolbar: 'variables | undo redo | formatselect | ' +
'bold italic | alignleft aligncenter alignright | ' +
'code preview',
setup: handleEditorSetup
};
return (
<div className="p-6 bg-white dark:bg-gray-800 rounded-lg shadow">
<h2 className="text-xl font-bold mb-6">Configuração do Papel Timbrado</h2>
<div className="space-y-6">
{/* Upload do Timbrado */}
<div>
<label className="block mb-2">Papel Timbrado</label>
<div className="flex items-center gap-4">
<label className="flex items-center px-4 py-2 bg-blue-600 text-white rounded cursor-pointer hover:bg-blue-700">
<DocumentArrowUpIcon className="w-5 h-5 mr-2" />
Fazer Upload
<input
type="file"
className="hidden"
accept=".pdf,.png,.svg"
onChange={handleFileUpload}
/>
</label>
{config.timbradoUrl && (
<button
onClick={() => setPreviewOpen(true)}
className="text-blue-600 hover:underline"
>
Visualizar
</button>
)}
</div>
<p className="mt-2 text-sm text-gray-500">
Use arquivos PDF, PNG ou SVG. Tamanho máximo: 5MB
</p>
</div>
{/* Margens */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block mb-2">Margem Superior (px)</label>
<input
type="number"
value={config.timbradoMarginTop}
onChange={e => setConfig(prev => ({ ...prev, timbradoMarginTop: Number(e.target.value) }))}
className="w-full p-2 border rounded"
/>
</div>
<div>
<label className="block mb-2">Margem Inferior (px)</label>
<input
type="number"
value={config.timbradoMarginBottom}
onChange={e => setConfig(prev => ({ ...prev, timbradoMarginBottom: Number(e.target.value) }))}
className="w-full p-2 border rounded"
/>
</div>
<div>
<label className="block mb-2">Margem Esquerda (px)</label>
<input
type="number"
value={config.timbradoMarginLeft}
onChange={e => setConfig(prev => ({ ...prev, timbradoMarginLeft: Number(e.target.value) }))}
className="w-full p-2 border rounded"
/>
</div>
<div>
<label className="block mb-2">Margem Direita (px)</label>
<input
type="number"
value={config.timbradoMarginRight}
onChange={e => setConfig(prev => ({ ...prev, timbradoMarginRight: Number(e.target.value) }))}
className="w-full p-2 border rounded"
/>
</div>
</div>
{/* Template Padrão */}
<div className="mt-6">
<label className="block mb-2">Template Padrão</label>
<Editor
apiKey={import.meta.env.VITE_TINYMCE_API_KEY}
value={config.templatePadrao}
onEditorChange={(content) => setConfig(prev => ({ ...prev, templatePadrao: content }))}
init={editorConfig}
/>
<div className="mt-2 p-4 bg-gray-50 rounded border">
<h4 className="font-medium mb-2">Variáveis Disponíveis:</h4>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{Object.entries(DOCUMENT_VARIABLES).map(([category, vars]) => (
<div key={category}>
<h5 className="font-medium text-sm text-gray-700">{category}</h5>
<ul className="mt-1 space-y-1">
{Object.entries(vars).map(([name, value]) => (
<li key={name} className="text-xs text-gray-600">
{value as string}
</li>
))}
</ul>
</div>
))}
</div>
</div>
</div>
{/* Margens do Template */}
<div className="mt-6 grid grid-cols-2 gap-4">
<div>
<label className="block mb-2">Margem Superior (mm)</label>
<input
type="number"
value={config.margensTemplate.top}
onChange={e => setConfig(prev => ({
...prev,
margensTemplate: {
...prev.margensTemplate,
top: Number(e.target.value)
}
}))}
className="w-full p-2 border rounded"
/>
</div>
<div>
<label className="block mb-2">Margem Inferior (mm)</label>
<input
type="number"
value={config.margensTemplate.bottom}
onChange={e => setConfig(prev => ({
...prev,
margensTemplate: {
...prev.margensTemplate,
bottom: Number(e.target.value)
}
}))}
className="w-full p-2 border rounded"
/>
</div>
<div>
<label className="block mb-2">Margem Esquerda (mm)</label>
<input
type="number"
value={config.margensTemplate.left}
onChange={e => setConfig(prev => ({
...prev,
margensTemplate: {
...prev.margensTemplate,
left: Number(e.target.value)
}
}))}
className="w-full p-2 border rounded"
/>
</div>
<div>
<label className="block mb-2">Margem Direita (mm)</label>
<input
type="number"
value={config.margensTemplate.right}
onChange={e => setConfig(prev => ({
...prev,
margensTemplate: {
...prev.margensTemplate,
right: Number(e.target.value)
}
}))}
className="w-full p-2 border rounded"
/>
</div>
</div>
<button
onClick={handleSave}
disabled={isSaving}
className={`px-4 py-2 ${
isSaving
? 'bg-gray-400 cursor-not-allowed'
: 'bg-blue-600 hover:bg-blue-700'
} text-white rounded`}
>
{isSaving ? 'Salvando...' : 'Salvar Configurações'}
</button>
</div>
{/* Modal de Preview */}
<Dialog open={isPreviewOpen} onClose={() => setPreviewOpen(false)}>
<div className="fixed inset-0 bg-black/30" aria-hidden="true" />
<div className="fixed inset-0 flex items-center justify-center p-4">
<Dialog.Panel className="w-full max-w-3xl bg-white rounded-lg shadow-xl p-6">
<h3 className="text-lg font-semibold mb-4">Preview do Timbrado</h3>
<div className="border rounded p-4 bg-gray-50">
<img
src={config.timbradoUrl}
alt="Timbrado"
className="max-w-full h-auto"
/>
</div>
<button
onClick={() => setPreviewOpen(false)}
className="mt-4 px-4 py-2 bg-gray-200 rounded hover:bg-gray-300"
>
Fechar
</button>
</Dialog.Panel>
</div>
</Dialog>
</div>
);
}