384 lines
12 KiB
TypeScript
384 lines
12 KiB
TypeScript
import { Dialog } from "@headlessui/react";
|
|
import { DocumentArrowUpIcon } from "@heroicons/react/24/outline";
|
|
import { Editor } from "@tinymce/tinymce-react";
|
|
import { useState, useEffect } from "react";
|
|
import { toast } from "react-toastify";
|
|
|
|
import { api } from "../../lib/api";
|
|
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>
|
|
);
|
|
}
|