Initial commit DBMaker - Oficiais e Funcionando
This commit is contained in:
982
estrutura_visual/exported-assets/exemplos-codigo-template.md
Normal file
982
estrutura_visual/exported-assets/exemplos-codigo-template.md
Normal file
@@ -0,0 +1,982 @@
|
||||
# Exemplos de Código - Template Databook SteelBook
|
||||
|
||||
## 1. Estrutura HTML - Capa Frontal
|
||||
|
||||
```html
|
||||
<!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
|
||||
|
||||
```html
|
||||
<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
|
||||
|
||||
```html
|
||||
<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
|
||||
|
||||
```html
|
||||
<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é
|
||||
|
||||
```html
|
||||
<!-- 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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```css
|
||||
: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)
|
||||
|
||||
```sql
|
||||
-- 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)
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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.
|
||||
Reference in New Issue
Block a user