24 KiB
24 KiB
Exemplos de Código - Template Databook SteelBook
1. Estrutura HTML - Capa Frontal
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Databook - Capa Frontal</title>
<style>
.cover-page {
width: 210mm;
height: 297mm;
padding: 40mm 30mm;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
font-family: 'Roboto', Arial, sans-serif;
}
.cover-header {
text-align: center;
}
.client-logo {
width: 200px;
height: 100px;
object-fit: contain;
margin-bottom: 30px;
}
.cover-title {
font-size: 60px;
font-weight: bold;
color: #1a365d;
line-height: 1.2;
margin-bottom: 20px;
}
.cover-subtitle {
font-size: 36px;
color: #2d3748;
margin-bottom: 40px;
}
.cover-separator {
width: 60%;
height: 3px;
background: #2b6cb0;
margin: 30px 0;
}
.cover-info {
text-align: center;
font-size: 24px;
color: #4a5568;
}
.cover-info strong {
color: #1a365d;
display: block;
margin-top: 15px;
}
.supplier-logo {
width: 150px;
height: 75px;
object-fit: contain;
}
</style>
</head>
<body>
<div class="cover-page">
<div class="cover-header">
<img src="{{client_logo}}" alt="Logo Cliente" class="client-logo">
<h1 class="cover-title">{{project_title}}</h1>
<h2 class="cover-subtitle">{{project_subtitle}}</h2>
<div class="cover-separator"></div>
</div>
<div class="cover-info">
<strong>Documento: {{document_number}}</strong>
<strong>Contrato: {{contract_number}}</strong>
<strong>Data: {{issue_date}}</strong>
</div>
<img src="{{supplier_logo}}" alt="Logo Fornecedor" class="supplier-logo">
</div>
</body>
</html>
2. Estrutura HTML - Índice Geral
<div class="index-page">
<h1 class="index-title">ÍNDICE / TABLE OF CONTENTS</h1>
<div class="index-separator"></div>
<div class="index-content">
<div class="index-item level-1">
<span class="index-number">1</span>
<span class="index-title-pt">Identificação</span>
<span class="index-dots"></span>
<span class="index-page">3</span>
</div>
<div class="index-item-en">Identification</div>
<div class="index-item level-1">
<span class="index-number">2</span>
<span class="index-title-pt">Materiais</span>
<span class="index-dots"></span>
<span class="index-page">5</span>
</div>
<div class="index-item-en">Materials</div>
<div class="index-item level-2">
<span class="index-number">2.1</span>
<span class="index-title-pt">Certificados das matérias-primas</span>
<span class="index-dots"></span>
<span class="index-page">6</span>
</div>
<div class="index-item-en">Raw materials certificates</div>
<div class="index-item level-2">
<span class="index-number">2.2</span>
<span class="index-title-pt">Consumíveis de soldagem</span>
<span class="index-dots"></span>
<span class="index-page">12</span>
</div>
<div class="index-item-en">Welding consumables</div>
</div>
</div>
<style>
.index-page {
width: 210mm;
min-height: 297mm;
padding: 30mm 25mm;
font-family: 'Open Sans', Arial, sans-serif;
}
.index-title {
font-size: 36px;
font-weight: bold;
color: #1a365d;
text-align: center;
margin-bottom: 20px;
}
.index-separator {
width: 100%;
height: 2px;
background: #2b6cb0;
margin-bottom: 40px;
}
.index-item {
display: flex;
align-items: baseline;
margin-bottom: 8px;
font-size: 18px;
color: #2d3748;
}
.index-item.level-1 {
font-weight: bold;
color: #1a365d;
font-size: 20px;
margin-top: 20px;
}
.index-item.level-2 {
padding-left: 20px;
font-size: 18px;
}
.index-item.level-3 {
padding-left: 40px;
font-size: 16px;
}
.index-number {
min-width: 40px;
font-weight: bold;
}
.index-title-pt {
flex: 1;
}
.index-dots {
flex: 1;
border-bottom: 1px dotted #cbd5e0;
margin: 0 10px;
}
.index-page {
min-width: 40px;
text-align: right;
font-weight: bold;
}
.index-item-en {
font-size: 16px;
color: #718096;
font-style: italic;
margin-left: 40px;
margin-bottom: 15px;
}
</style>
3. Estrutura HTML - Divisora Estilo Minimalista
<div class="divider-minimal">
<div class="divider-number-watermark">2</div>
<div class="divider-content">
<h1 class="divider-title">Materiais</h1>
<h2 class="divider-subtitle">Materials</h2>
<div class="divider-line"></div>
<p class="divider-description">
Certificados, especificações e documentação de matérias-primas
</p>
</div>
</div>
<style>
.divider-minimal {
width: 210mm;
height: 297mm;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: #ffffff;
}
.divider-number-watermark {
position: absolute;
font-size: 400px;
font-weight: bold;
color: #1a365d;
opacity: 0.05;
z-index: 1;
line-height: 1;
}
.divider-content {
position: relative;
z-index: 2;
text-align: center;
}
.divider-title {
font-size: 72px;
font-weight: bold;
color: #1a365d;
margin-bottom: 20px;
}
.divider-subtitle {
font-size: 36px;
color: #718096;
font-style: italic;
margin-bottom: 40px;
}
.divider-line {
width: 200px;
height: 4px;
background: linear-gradient(90deg, transparent, #2b6cb0, transparent);
margin: 0 auto 30px;
}
.divider-description {
font-size: 20px;
color: #4a5568;
}
</style>
4. Estrutura HTML - Divisora Estilo Lateral
<div class="divider-lateral">
<div class="divider-sidebar">
<span class="divider-sidebar-number">3</span>
<span class="divider-sidebar-icon">⚡</span>
</div>
<div class="divider-main">
<h1 class="divider-main-title">Procedimentos de Soldagem</h1>
<h2 class="divider-main-subtitle">Welding Procedures</h2>
<div class="divider-info-box">
<p><strong>Projeto:</strong> BUZIOS 7</p>
<p><strong>Cliente:</strong> SAIPEM</p>
<p><strong>Contrato:</strong> OC 1472739</p>
</div>
</div>
</div>
<style>
.divider-lateral {
width: 210mm;
height: 297mm;
display: flex;
background: #f7fafc;
}
.divider-sidebar {
width: 80px;
background: linear-gradient(180deg, #1a365d 0%, #2b6cb0 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
}
.divider-sidebar-number {
font-size: 96px;
font-weight: bold;
line-height: 1;
margin-bottom: 30px;
}
.divider-sidebar-icon {
font-size: 48px;
}
.divider-main {
flex: 1;
padding: 80px 60px;
display: flex;
flex-direction: column;
justify-content: center;
}
.divider-main-title {
font-size: 56px;
font-weight: bold;
color: #1a365d;
margin-bottom: 15px;
line-height: 1.2;
}
.divider-main-subtitle {
font-size: 32px;
color: #718096;
font-style: italic;
margin-bottom: 60px;
}
.divider-info-box {
background: white;
border: 2px solid #2b6cb0;
border-radius: 8px;
padding: 30px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.divider-info-box p {
font-size: 18px;
color: #2d3748;
margin: 10px 0;
}
.divider-info-box strong {
color: #1a365d;
}
</style>
5. Estrutura HTML - Cabeçalho e Rodapé
<!-- CABEÇALHO -->
<div class="page-header">
<div class="header-left">
<img src="{{logo_url}}" alt="Logo" class="header-logo">
</div>
<div class="header-center">
<span class="header-project">{{project_name}}</span>
</div>
<div class="header-right">
<span class="header-doc">{{document_number}}</span>
</div>
</div>
<!-- RODAPÉ -->
<div class="page-footer">
<div class="footer-left">
<span>Rev. {{revision}}</span>
</div>
<div class="footer-center">
<span class="page-number">{{page_number}}</span>
</div>
<div class="footer-right">
<span>{{date}}</span>
</div>
</div>
<style>
.page-header {
width: 100%;
height: 60px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30px;
border-bottom: 2px solid #2b6cb0;
background: white;
font-family: 'Roboto', Arial, sans-serif;
}
.header-logo {
height: 40px;
object-fit: contain;
}
.header-center {
flex: 1;
text-align: center;
font-size: 14px;
font-weight: 500;
color: #1a365d;
}
.header-right {
font-size: 12px;
color: #718096;
}
.page-footer {
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30px;
border-top: 1px solid #cbd5e0;
background: white;
font-size: 12px;
color: #4a5568;
}
.footer-center .page-number {
font-size: 18px;
font-weight: bold;
color: #1a365d;
}
</style>
6. JavaScript - Sistema de Variáveis Dinâmicas
// Classe para gerenciar templates
class DatabookTemplate {
constructor(config) {
this.config = config;
this.variables = {};
}
// Define variáveis do projeto
setVariables(vars) {
this.variables = { ...this.variables, ...vars };
}
// Renderiza template substituindo variáveis
render(templateHtml) {
let rendered = templateHtml;
// Substitui variáveis no formato {{variable_name}}
for (const [key, value] of Object.entries(this.variables)) {
const regex = new RegExp(`{{${key}}}`, 'g');
rendered = rendered.replace(regex, value);
}
return rendered;
}
// Gera capa frontal
generateCover() {
const coverTemplate = `
<div class="cover-page">
<img src="{{client_logo}}" class="client-logo">
<h1>{{project_title}}</h1>
<h2>{{project_subtitle}}</h2>
<div class="cover-info">
<p>Documento: {{document_number}}</p>
<p>Contrato: {{contract_number}}</p>
<p>Data: {{issue_date}}</p>
</div>
<img src="{{supplier_logo}}" class="supplier-logo">
</div>
`;
return this.render(coverTemplate);
}
// Gera índice
generateIndex(sections) {
let indexHtml = '<div class="index-page"><h1>ÍNDICE / TABLE OF CONTENTS</h1>';
sections.forEach(section => {
const indent = section.level > 1 ? `level-${section.level}` : '';
indexHtml += `
<div class="index-item ${indent}">
<span class="index-number">${section.number}</span>
<span class="index-title">${section.title_pt}</span>
<span class="index-dots"></span>
<span class="index-page">${section.page}</span>
</div>
<div class="index-item-en">${section.title_en}</div>
`;
});
indexHtml += '</div>';
return indexHtml;
}
// Gera divisora de seção
generateDivider(sectionNumber, titlePt, titleEn, style = 'minimal') {
const templates = {
minimal: `
<div class="divider-minimal">
<div class="divider-number-watermark">${sectionNumber}</div>
<div class="divider-content">
<h1>${titlePt}</h1>
<h2>${titleEn}</h2>
</div>
</div>
`,
lateral: `
<div class="divider-lateral">
<div class="divider-sidebar">
<span>${sectionNumber}</span>
</div>
<div class="divider-main">
<h1>${titlePt}</h1>
<h2>${titleEn}</h2>
</div>
</div>
`,
corporate: `
<div class="divider-corporate">
<div class="divider-header">
<img src="{{client_logo}}">
</div>
<div class="divider-body">
<span class="section-number">${sectionNumber}</span>
<h1>${titlePt}</h1>
<h2>${titleEn}</h2>
</div>
</div>
`
};
return this.render(templates[style] || templates.minimal);
}
}
// Exemplo de uso
const template = new DatabookTemplate({
name: 'SAIPEM Vendor Databook',
version: '1.0'
});
template.setVariables({
project_title: 'BUZIOS 7 PRODUCTION SYSTEM DEVELOPMENT',
project_subtitle: 'AR HEAD FABRICATION LONG',
client_logo: '/logos/saipem.png',
supplier_logo: '/logos/engemetal.png',
document_number: 'DB-B97-01_S1_VENDOR_DATABOOK',
contract_number: 'OC 1472739',
issue_date: '2024-11-17'
});
// Gerar componentes
const cover = template.generateCover();
const index = template.generateIndex([
{ number: '1', title_pt: 'Identificação', title_en: 'Identification', level: 1, page: 3 },
{ number: '2', title_pt: 'Materiais', title_en: 'Materials', level: 1, page: 5 }
]);
const divider = template.generateDivider('2', 'Materiais', 'Materials', 'minimal');
7. CSS - Variáveis Customizáveis
:root {
/* Cores Primárias */
--color-primary: #1a365d;
--color-secondary: #2b6cb0;
--color-accent: #4299e1;
/* Cores Neutras */
--color-gray-dark: #2d3748;
--color-gray-medium: #718096;
--color-gray-light: #e2e8f0;
/* Tipografia */
--font-heading: 'Roboto', Arial, sans-serif;
--font-body: 'Open Sans', Arial, sans-serif;
--font-mono: 'Roboto Mono', monospace;
/* Tamanhos de Fonte */
--font-size-h1: 60px;
--font-size-h2: 48px;
--font-size-h3: 36px;
--font-size-h4: 24px;
--font-size-body: 16px;
--font-size-small: 12px;
/* Espaçamentos */
--spacing-xs: 8px;
--spacing-sm: 16px;
--spacing-md: 24px;
--spacing-lg: 40px;
--spacing-xl: 60px;
/* Bordas */
--border-radius: 8px;
--border-width: 2px;
}
/* Aplicação das variáveis */
.cover-title {
color: var(--color-primary);
font-family: var(--font-heading);
font-size: var(--font-size-h1);
}
.index-item {
color: var(--color-gray-dark);
font-family: var(--font-body);
padding: var(--spacing-sm) 0;
}
.divider-content {
padding: var(--spacing-xl);
}
/* Tema alternativo - Azul Claro */
.theme-light {
--color-primary: #2563eb;
--color-secondary: #60a5fa;
--color-accent: #93c5fd;
}
/* Tema alternativo - Cinza Profissional */
.theme-professional {
--color-primary: #334155;
--color-secondary: #64748b;
--color-accent: #94a3b8;
}
8. Integração com Banco de Dados (Supabase)
-- Tabela de Templates
CREATE TABLE templates (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
nome VARCHAR(255) NOT NULL,
descricao TEXT,
versao VARCHAR(20) DEFAULT '1.0',
config JSONB NOT NULL,
criado_por UUID REFERENCES auth.users(id),
criado_em TIMESTAMP DEFAULT NOW(),
atualizado_em TIMESTAMP DEFAULT NOW(),
ativo BOOLEAN DEFAULT true,
publico BOOLEAN DEFAULT false,
organizacao_id UUID REFERENCES organizacoes(id)
);
-- Exemplo de config JSONB
{
"capa": {
"titulo": "{{project_title}}",
"subtitulo": "{{project_subtitle}}",
"cores": {
"primaria": "#1a365d",
"secundaria": "#2b6cb0"
}
},
"indice": {
"bilingue": true,
"estrutura": [
{
"numero": "1",
"titulo_pt": "Identificação",
"titulo_en": "Identification",
"nivel": 1
}
]
},
"divisoras": {
"estilo": "minimal"
}
}
-- Tabela de Databooks usando Templates
CREATE TABLE databooks (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
nome VARCHAR(255) NOT NULL,
template_id UUID REFERENCES templates(id),
dados JSONB NOT NULL,
criado_em TIMESTAMP DEFAULT NOW()
);
-- Query para buscar templates
SELECT t.*, u.nome as criador
FROM templates t
LEFT JOIN auth.users u ON t.criado_por = u.id
WHERE t.ativo = true
AND (t.publico = true OR t.organizacao_id = $1)
ORDER BY t.criado_em DESC;
-- Query para aplicar template a databook
UPDATE databooks
SET template_id = $1,
dados = jsonb_set(dados, '{template_aplicado}', 'true'::jsonb)
WHERE id = $2;
9. API Endpoints (Node.js/Express)
const express = require('express');
const router = express.Router();
// GET /api/templates - Lista templates
router.get('/templates', async (req, res) => {
try {
const { data, error } = await supabase
.from('templates')
.select('*')
.eq('ativo', true)
.order('criado_em', { ascending: false });
if (error) throw error;
res.json({ success: true, data });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// POST /api/templates - Cria novo template
router.post('/templates', async (req, res) => {
try {
const { nome, descricao, config } = req.body;
const { data, error } = await supabase
.from('templates')
.insert([{
nome,
descricao,
config,
criado_por: req.user.id
}])
.select()
.single();
if (error) throw error;
res.json({ success: true, data });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// PUT /api/templates/:id - Atualiza template
router.put('/templates/:id', async (req, res) => {
try {
const { id } = req.params;
const { nome, descricao, config } = req.body;
const { data, error } = await supabase
.from('templates')
.update({
nome,
descricao,
config,
atualizado_em: new Date().toISOString()
})
.eq('id', id)
.select()
.single();
if (error) throw error;
res.json({ success: true, data });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// POST /api/databooks/:id/apply-template - Aplica template
router.post('/databooks/:id/apply-template', async (req, res) => {
try {
const { id } = req.params;
const { template_id } = req.body;
// Buscar template
const { data: template } = await supabase
.from('templates')
.select('*')
.eq('id', template_id)
.single();
// Buscar databook
const { data: databook } = await supabase
.from('databooks')
.select('*')
.eq('id', id)
.single();
// Aplicar configurações do template
const dadosAtualizados = {
...databook.dados,
template_config: template.config
};
// Atualizar databook
const { data, error } = await supabase
.from('databooks')
.update({
template_id,
dados: dadosAtualizados
})
.eq('id', id)
.select()
.single();
if (error) throw error;
res.json({ success: true, data });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
module.exports = router;
10. Geração de PDF com Puppeteer
const puppeteer = require('puppeteer');
const fs = require('fs');
class DatabookPDFGenerator {
constructor(template, databook) {
this.template = template;
this.databook = databook;
}
async generatePDF(outputPath) {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
});
const page = await browser.newPage();
// Configurar página
await page.setViewport({
width: 794, // A4 width in pixels at 96 DPI
height: 1123 // A4 height in pixels at 96 DPI
});
// Gerar HTML completo
const htmlContent = this.generateFullHTML();
// Carregar conteúdo
await page.setContent(htmlContent, {
waitUntil: 'networkidle0'
});
// Gerar PDF
await page.pdf({
path: outputPath,
format: 'A4',
printBackground: true,
margin: {
top: '20mm',
right: '15mm',
bottom: '20mm',
left: '15mm'
},
displayHeaderFooter: true,
headerTemplate: this.generateHeader(),
footerTemplate: this.generateFooter()
});
await browser.close();
return outputPath;
}
generateFullHTML() {
const sections = [];
// Capa
sections.push(this.generateCover());
// Índice
sections.push(this.generateIndex());
// Seções com divisoras
this.databook.sections.forEach(section => {
sections.push(this.generateDivider(section));
sections.push(this.generateSectionContent(section));
});
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>${this.getStyles()}</style>
</head>
<body>
${sections.join('\n<div class="page-break"></div>\n')}
</body>
</html>
`;
}
generateHeader() {
return `
<div style="font-size: 10px; padding: 0 20px; width: 100%;">
<span>${this.databook.project_name}</span>
</div>
`;
}
generateFooter() {
return `
<div style="font-size: 10px; padding: 0 20px; width: 100%; text-align: center;">
<span class="pageNumber"></span> / <span class="totalPages"></span>
</div>
`;
}
getStyles() {
return fs.readFileSync('./styles/databook.css', 'utf8');
}
}
// Uso
const generator = new DatabookPDFGenerator(template, databook);
await generator.generatePDF('./output/databook.pdf');
Conclusão
Estes exemplos fornecem uma base sólida para implementar o sistema de templates no SteelBook. Adapte conforme necessário para sua arquitetura específica.