Initial commit DBMaker - Oficiais e Funcionando
This commit is contained in:
386
src/components/design/TemplateEditor.tsx
Normal file
386
src/components/design/TemplateEditor.tsx
Normal file
@@ -0,0 +1,386 @@
|
||||
|
||||
import Input from '@/components/common/Input'
|
||||
|
||||
interface TemplateEditorProps {
|
||||
tipo: string
|
||||
config: Record<string, any>
|
||||
onChange: (config: Record<string, any>) => void
|
||||
}
|
||||
|
||||
export default function TemplateEditor({ tipo, config, onChange }: TemplateEditorProps) {
|
||||
const handleColorChange = (key: string, value: string) => {
|
||||
onChange({ ...config, [key]: value })
|
||||
}
|
||||
|
||||
const handleTextChange = (key: string, value: string) => {
|
||||
onChange({ ...config, [key]: value })
|
||||
}
|
||||
|
||||
const renderEditorByType = () => {
|
||||
switch (tipo) {
|
||||
case 'capa':
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor Primária
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corPrimaria || '#1a365d'}
|
||||
onChange={(e) => handleColorChange('corPrimaria', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor Secundária
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corSecundaria || '#2b6cb0'}
|
||||
onChange={(e) => handleColorChange('corSecundaria', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
label="Título Principal"
|
||||
value={config.titulo || ''}
|
||||
onChange={(e) => handleTextChange('titulo', e.target.value)}
|
||||
placeholder="Ex: BUZIOS 7 PRODUCTION SYSTEM"
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Subtítulo"
|
||||
value={config.subtitulo || ''}
|
||||
onChange={(e) => handleTextChange('subtitulo', e.target.value)}
|
||||
placeholder="Ex: AR HEAD FABRICATION"
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Cliente"
|
||||
value={config.cliente || ''}
|
||||
onChange={(e) => handleTextChange('cliente', e.target.value)}
|
||||
placeholder="Ex: SAIPEM"
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Número do Documento"
|
||||
value={config.numeroDocumento || ''}
|
||||
onChange={(e) => handleTextChange('numeroDocumento', e.target.value)}
|
||||
placeholder="Ex: DB-B97-01_S1_VENDOR_DATABOOK"
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Contrato"
|
||||
value={config.contrato || ''}
|
||||
onChange={(e) => handleTextChange('contrato', e.target.value)}
|
||||
placeholder="Ex: OC 1472739"
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Fornecedor"
|
||||
value={config.fornecedor || ''}
|
||||
onChange={(e) => handleTextChange('fornecedor', e.target.value)}
|
||||
placeholder="Ex: ENGEMETAL"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'indice':
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor do Título
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corTitulo || '#1a365d'}
|
||||
onChange={(e) => handleColorChange('corTitulo', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer bg-white dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor da Linha Divisória
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corLinha || '#2b6cb0'}
|
||||
onChange={(e) => handleColorChange('corLinha', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer bg-white dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.bilingue || false}
|
||||
onChange={(e) => handleColorChange('bilingue', e.target.checked ? 'true' : 'false')}
|
||||
className="rounded border-gray-300 dark:border-gray-600"
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Bilíngue (PT/EN)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
label="Título do Índice"
|
||||
value={config.titulo || 'ÍNDICE / TABLE OF CONTENTS'}
|
||||
onChange={(e) => handleTextChange('titulo', e.target.value)}
|
||||
placeholder="Título do índice"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'divisora':
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Estilo da Divisora
|
||||
</label>
|
||||
<select
|
||||
value={config.estilo || 'minimalista'}
|
||||
onChange={(e) => handleColorChange('estilo', e.target.value)}
|
||||
className="input-field"
|
||||
>
|
||||
<option value="minimalista">Minimalista</option>
|
||||
<option value="lateral">Lateral</option>
|
||||
<option value="corporativa">Corporativa</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor Primária
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corPrimaria || '#1a365d'}
|
||||
onChange={(e) => handleColorChange('corPrimaria', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer bg-white dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor Secundária
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corSecundaria || '#2b6cb0'}
|
||||
onChange={(e) => handleColorChange('corSecundaria', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer bg-white dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.bilingue || false}
|
||||
onChange={(e) => handleColorChange('bilingue', e.target.checked ? 'true' : 'false')}
|
||||
className="rounded border-gray-300 dark:border-gray-600"
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Bilíngue (PT/EN)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
label="Ícone da Seção"
|
||||
value={config.icone || '📑'}
|
||||
onChange={(e) => handleTextChange('icone', e.target.value)}
|
||||
placeholder="Ex: 📑, 🔩, ⚡"
|
||||
maxLength={2}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'cabecalho':
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor da Borda
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corBorda || '#2b6cb0'}
|
||||
onChange={(e) => handleColorChange('corBorda', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer bg-white dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
label="Altura (px)"
|
||||
type="number"
|
||||
value={config.altura || 60}
|
||||
onChange={(e) => handleTextChange('altura', e.target.value)}
|
||||
placeholder="60"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Estilo
|
||||
</label>
|
||||
<select
|
||||
value={config.estilo || 'simples'}
|
||||
onChange={(e) => handleColorChange('estilo', e.target.value)}
|
||||
className="input-field"
|
||||
>
|
||||
<option value="simples">Simples</option>
|
||||
<option value="completo">Completo com Logo</option>
|
||||
<option value="minimalista">Minimalista</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'rodape':
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor da Borda
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corBorda || '#cbd5e0'}
|
||||
onChange={(e) => handleColorChange('corBorda', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer bg-white dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
label="Altura (px)"
|
||||
type="number"
|
||||
value={config.altura || 40}
|
||||
onChange={(e) => handleTextChange('altura', e.target.value)}
|
||||
placeholder="40"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Estilo
|
||||
</label>
|
||||
<select
|
||||
value={config.estilo || 'simples'}
|
||||
onChange={(e) => handleColorChange('estilo', e.target.value)}
|
||||
className="input-field"
|
||||
>
|
||||
<option value="simples">Simples</option>
|
||||
<option value="completo">Completo</option>
|
||||
<option value="minimalista">Minimalista</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.mostrarPagina || true}
|
||||
onChange={(e) => handleColorChange('mostrarPagina', e.target.checked ? 'true' : 'false')}
|
||||
className="rounded border-gray-300 dark:border-gray-600"
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Mostrar Número da Página</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'guia_estilo':
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor Primária
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corPrimaria || '#1a365d'}
|
||||
onChange={(e) => handleColorChange('corPrimaria', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer bg-white dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor Secundária
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corSecundaria || '#2b6cb0'}
|
||||
onChange={(e) => handleColorChange('corSecundaria', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer bg-white dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Cor de Destaque
|
||||
</label>
|
||||
<input
|
||||
type="color"
|
||||
value={config.corDestaque || '#4299e1'}
|
||||
onChange={(e) => handleColorChange('corDestaque', e.target.value)}
|
||||
className="w-full h-10 rounded border border-gray-300 dark:border-gray-600 cursor-pointer bg-white dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
label="Fonte Principal"
|
||||
value={config.fontePrincipal || 'Roboto'}
|
||||
onChange={(e) => handleTextChange('fontePrincipal', e.target.value)}
|
||||
placeholder="Ex: Roboto, Arial"
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Fonte Secundária"
|
||||
value={config.fonteSecundaria || 'Open Sans'}
|
||||
onChange={(e) => handleTextChange('fonteSecundaria', e.target.value)}
|
||||
placeholder="Ex: Open Sans, Helvetica"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.incluirPaleta || true}
|
||||
onChange={(e) => handleColorChange('incluirPaleta', e.target.checked ? 'true' : 'false')}
|
||||
className="rounded border-gray-300 dark:border-gray-600"
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Incluir Paleta de Cores</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.incluirTipografia || true}
|
||||
onChange={(e) => handleColorChange('incluirTipografia', e.target.checked ? 'true' : 'false')}
|
||||
className="rounded border-gray-300 dark:border-gray-600"
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Incluir Tipografia</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
default:
|
||||
return <p className="text-gray-500 dark:text-gray-400">Selecione um tipo de template</p>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-gray-100">Configurações do Template</h3>
|
||||
{renderEditorByType()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
394
src/components/design/TemplatePreview.tsx
Normal file
394
src/components/design/TemplatePreview.tsx
Normal file
@@ -0,0 +1,394 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { ZoomIn, ZoomOut, Maximize2 } from 'lucide-react'
|
||||
|
||||
interface TemplatePreviewProps {
|
||||
tipo: string
|
||||
config: Record<string, any>
|
||||
}
|
||||
|
||||
export default function TemplatePreview({ tipo, config }: TemplatePreviewProps) {
|
||||
const [zoom, setZoom] = useState(30)
|
||||
const [isPanning, setIsPanning] = useState(false)
|
||||
const [panStart, setPanStart] = useState({ x: 0, y: 0 })
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const handleZoomIn = () => setZoom(prev => Math.min(prev + 10, 200))
|
||||
const handleZoomOut = () => setZoom(prev => Math.max(prev - 10, 30))
|
||||
const handleResetZoom = () => setZoom(100)
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
if (!scrollContainerRef.current) return
|
||||
setIsPanning(true)
|
||||
setPanStart({
|
||||
x: e.clientX + scrollContainerRef.current.scrollLeft,
|
||||
y: e.clientY + scrollContainerRef.current.scrollTop,
|
||||
})
|
||||
}
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent) => {
|
||||
if (!isPanning || !scrollContainerRef.current) return
|
||||
|
||||
scrollContainerRef.current.scrollLeft = panStart.x - e.clientX
|
||||
scrollContainerRef.current.scrollTop = panStart.y - e.clientY
|
||||
}
|
||||
|
||||
const handleMouseUp = () => {
|
||||
setIsPanning(false)
|
||||
}
|
||||
|
||||
const renderPreview = () => {
|
||||
const corPrimaria = config.corPrimaria || '#1a365d'
|
||||
const corSecundaria = config.corSecundaria || '#2b6cb0'
|
||||
|
||||
switch (tipo) {
|
||||
case 'capa':
|
||||
return (
|
||||
<div
|
||||
className="bg-white shadow-2xl flex flex-col justify-between p-8"
|
||||
style={{
|
||||
width: '210mm',
|
||||
height: '297mm',
|
||||
background: `linear-gradient(135deg, rgba(${parseInt(corPrimaria.slice(1, 3), 16)}, ${parseInt(corPrimaria.slice(3, 5), 16)}, ${parseInt(corPrimaria.slice(5, 7), 16)}, 0.05), rgba(${parseInt(corSecundaria.slice(1, 3), 16)}, ${parseInt(corSecundaria.slice(3, 5), 16)}, ${parseInt(corSecundaria.slice(5, 7), 16)}, 0.05))`,
|
||||
}}
|
||||
>
|
||||
<div className="text-center">
|
||||
<div className="w-32 h-16 bg-gray-300 rounded mx-auto mb-8 flex items-center justify-center text-gray-500 text-sm">
|
||||
Logo Cliente
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center flex-1 flex flex-col justify-center">
|
||||
<h1
|
||||
className="text-4xl font-bold mb-4 line-clamp-3"
|
||||
style={{ color: corPrimaria }}
|
||||
>
|
||||
{config.titulo || 'TÍTULO DO PROJETO'}
|
||||
</h1>
|
||||
<h2 className="text-2xl text-gray-700 mb-6">
|
||||
{config.subtitulo || 'Subtítulo do Projeto'}
|
||||
</h2>
|
||||
<div
|
||||
className="w-24 h-1 mx-auto mb-6"
|
||||
style={{ background: corSecundaria }}
|
||||
/>
|
||||
<div className="text-lg text-gray-600">
|
||||
<p className="font-semibold" style={{ color: corSecundaria }}>
|
||||
{config.numeroDocumento || 'DB-XXXX-XX_SX_VENDOR_DATABOOK'}
|
||||
</p>
|
||||
<p>Contrato: {config.contrato || 'OC XXXXXXX'}</p>
|
||||
<p>Cliente: {config.cliente || 'CLIENTE'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="w-24 h-12 bg-gray-300 rounded mx-auto flex items-center justify-center text-gray-500 text-xs">
|
||||
Logo {config.fornecedor || 'Fornecedor'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'indice':
|
||||
return (
|
||||
<div
|
||||
className="bg-white shadow-2xl overflow-y-auto p-8"
|
||||
style={{
|
||||
width: '210mm',
|
||||
height: '297mm',
|
||||
}}
|
||||
>
|
||||
<h1
|
||||
className="text-3xl font-bold text-center mb-2"
|
||||
style={{ color: corPrimaria }}
|
||||
>
|
||||
ÍNDICE
|
||||
</h1>
|
||||
{config.bilingue && (
|
||||
<p className="text-center text-gray-600 mb-4">TABLE OF CONTENTS</p>
|
||||
)}
|
||||
<div
|
||||
className="w-full h-1 mb-6"
|
||||
style={{ background: corSecundaria }}
|
||||
/>
|
||||
|
||||
<div className="space-y-2 text-sm">
|
||||
{[1, 2, 3, 4, 5].map((i) => (
|
||||
<div key={i} className="flex items-center justify-between">
|
||||
<span className="font-semibold" style={{ color: corSecundaria }}>
|
||||
{i}
|
||||
</span>
|
||||
<span className="flex-1 mx-2 border-b border-dotted border-gray-400" />
|
||||
<span className="font-semibold">{i * 5}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'divisora':
|
||||
const estilo = config.estilo || 'minimalista'
|
||||
|
||||
if (estilo === 'lateral') {
|
||||
return (
|
||||
<div
|
||||
className="bg-white shadow-2xl flex overflow-hidden"
|
||||
style={{
|
||||
width: '210mm',
|
||||
height: '297mm',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="w-20 flex items-center justify-center text-5xl font-bold text-white"
|
||||
style={{ background: `linear-gradient(180deg, ${corPrimaria}, ${corSecundaria})` }}
|
||||
>
|
||||
2
|
||||
</div>
|
||||
<div className="flex-1 p-8 flex flex-col justify-center">
|
||||
<h1 className="text-4xl font-bold mb-2" style={{ color: corPrimaria }}>
|
||||
Materiais
|
||||
</h1>
|
||||
{config.bilingue && (
|
||||
<h2 className="text-2xl text-gray-600 italic mb-6">Materials</h2>
|
||||
)}
|
||||
<div className="bg-gray-100 p-4 rounded text-sm text-gray-700">
|
||||
<p>
|
||||
<strong>Projeto:</strong> Projeto Exemplo
|
||||
</p>
|
||||
<p>
|
||||
<strong>Cliente:</strong> Cliente Exemplo
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="bg-white shadow-2xl flex flex-col items-center justify-center p-8 relative overflow-hidden"
|
||||
style={{
|
||||
width: '210mm',
|
||||
height: '297mm',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="absolute text-9xl font-bold opacity-5"
|
||||
style={{ color: corPrimaria }}
|
||||
>
|
||||
2
|
||||
</div>
|
||||
<div className="relative z-10 text-center">
|
||||
<p className="text-2xl mb-4" style={{ color: corSecundaria }}>
|
||||
{config.icone || '📑'} Seção 2
|
||||
</p>
|
||||
<h1 className="text-5xl font-bold mb-4" style={{ color: corPrimaria }}>
|
||||
Materiais
|
||||
</h1>
|
||||
{config.bilingue && (
|
||||
<h2 className="text-2xl text-gray-600 italic mb-6">Materials</h2>
|
||||
)}
|
||||
<div
|
||||
className="w-32 h-1 mx-auto"
|
||||
style={{ background: corSecundaria }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'cabecalho':
|
||||
return (
|
||||
<div
|
||||
className="bg-white shadow-2xl overflow-hidden"
|
||||
style={{
|
||||
width: '210mm',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="flex items-center justify-between p-4"
|
||||
style={{ borderBottom: `2px solid ${corSecundaria}` }}
|
||||
>
|
||||
<div className="w-16 h-8 bg-gray-300 rounded flex items-center justify-center text-xs text-gray-500">
|
||||
Logo
|
||||
</div>
|
||||
<span className="text-sm font-semibold text-gray-700">
|
||||
Projeto Exemplo
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 font-mono">
|
||||
DB-XXXX-XX_SX_VENDOR_DATABOOK
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'rodape':
|
||||
return (
|
||||
<div
|
||||
className="bg-white shadow-2xl overflow-hidden"
|
||||
style={{
|
||||
width: '210mm',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="flex items-center justify-between p-3 text-xs text-gray-600"
|
||||
style={{ borderTop: `1px solid ${config.corBorda || '#cbd5e0'}` }}
|
||||
>
|
||||
<span>Rev. 01 | 2024</span>
|
||||
<span className="font-bold text-lg" style={{ color: corPrimaria }}>
|
||||
12
|
||||
</span>
|
||||
<span>Fornecedor</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'guia_estilo':
|
||||
return (
|
||||
<div
|
||||
className="bg-white shadow-2xl p-8 overflow-y-auto"
|
||||
style={{
|
||||
width: '210mm',
|
||||
height: '297mm',
|
||||
}}
|
||||
>
|
||||
<h1
|
||||
className="text-3xl font-bold text-center mb-8"
|
||||
style={{ color: corPrimaria }}
|
||||
>
|
||||
Guia de Estilo
|
||||
</h1>
|
||||
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl font-bold mb-4" style={{ color: corPrimaria }}>
|
||||
Paleta de Cores
|
||||
</h2>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1">
|
||||
<div
|
||||
className="w-full h-16 rounded mb-2"
|
||||
style={{ backgroundColor: corPrimaria }}
|
||||
/>
|
||||
<p className="text-xs text-gray-600">Primária</p>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div
|
||||
className="w-full h-16 rounded mb-2"
|
||||
style={{ backgroundColor: corSecundaria }}
|
||||
/>
|
||||
<p className="text-xs text-gray-600">Secundária</p>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div
|
||||
className="w-full h-16 rounded mb-2"
|
||||
style={{ backgroundColor: config.corDestaque || '#4299e1' }}
|
||||
/>
|
||||
<p className="text-xs text-gray-600">Destaque</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-xl font-bold mb-4" style={{ color: corPrimaria }}>
|
||||
Tipografia
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
<div style={{ fontFamily: config.fontePrincipal || 'Roboto' }}>
|
||||
<p className="text-2xl font-bold">Título Principal</p>
|
||||
<p className="text-xs text-gray-600">
|
||||
{config.fontePrincipal || 'Roboto'}
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ fontFamily: config.fonteSecundaria || 'Open Sans' }}>
|
||||
<p className="text-base">Corpo de Texto</p>
|
||||
<p className="text-xs text-gray-600">
|
||||
{config.fonteSecundaria || 'Open Sans'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
default:
|
||||
return (
|
||||
<div
|
||||
className="bg-white shadow-2xl flex items-center justify-center"
|
||||
style={{
|
||||
width: '210mm',
|
||||
height: '297mm',
|
||||
}}
|
||||
>
|
||||
<p className="text-gray-500">Preview não disponível</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full space-y-2">
|
||||
{/* Controles de Zoom */}
|
||||
<div className="flex items-center justify-between bg-gray-100 dark:bg-gray-700 rounded-lg p-2 flex-shrink-0">
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={handleZoomOut}
|
||||
className="p-1 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors text-gray-700 dark:text-gray-300"
|
||||
title="Diminuir zoom"
|
||||
>
|
||||
<ZoomOut size={16} />
|
||||
</button>
|
||||
<span className="text-xs font-medium w-10 text-center text-gray-700 dark:text-gray-300">{zoom}%</span>
|
||||
<button
|
||||
onClick={handleZoomIn}
|
||||
className="p-1 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors text-gray-700 dark:text-gray-300"
|
||||
title="Aumentar zoom"
|
||||
>
|
||||
<ZoomIn size={16} />
|
||||
</button>
|
||||
<div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />
|
||||
<button
|
||||
onClick={handleResetZoom}
|
||||
className="p-1 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors flex items-center gap-1 text-gray-700 dark:text-gray-300"
|
||||
title="Resetar zoom"
|
||||
>
|
||||
<Maximize2 size={16} />
|
||||
<span className="text-xs">100%</span>
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-xs text-gray-600 dark:text-gray-400">
|
||||
A4 (210mm × 297mm)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Preview Container - Pan para navegar */}
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
className="flex-1 bg-gray-200 rounded-lg overflow-auto cursor-grab active:cursor-grabbing"
|
||||
style={{ minHeight: 0 }}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseLeave={handleMouseUp}
|
||||
>
|
||||
<div
|
||||
className="flex items-center justify-center p-3"
|
||||
style={{
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: `${210 * zoom / 100}mm`,
|
||||
height: `${297 * zoom / 100}mm`,
|
||||
transform: `scale(${zoom / 100})`,
|
||||
transformOrigin: 'center',
|
||||
transition: 'transform 0.2s ease-out',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
{renderPreview()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user