🚀 Auto-deploy: BrainWind atualizado em 15/07/2026 11:12:30
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Document, Page, Text, View, StyleSheet, Image as PdfImage, pdf, Font, Svg, Path } from '@react-pdf/renderer';
|
||||
import { Document, Page, Text, View, StyleSheet, Image as PdfImage, pdf, Font } from '@react-pdf/renderer';
|
||||
import { useWindStore } from '../store/appStore';
|
||||
import { useCaptureStore } from '../store/captureStore';
|
||||
|
||||
@@ -6,9 +6,9 @@ import { useCaptureStore } from '../store/captureStore';
|
||||
Font.register({
|
||||
family: 'Roboto',
|
||||
fonts: [
|
||||
{ src: 'https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.ttf' },
|
||||
{ src: 'https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1Mu4mxMyIFYW8YR8w.ttf', fontWeight: 'bold' },
|
||||
{ src: 'https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xIIzc.ttf', fontStyle: 'italic' },
|
||||
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Regular.ttf' },
|
||||
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Medium.ttf', fontWeight: 'bold' },
|
||||
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Italic.ttf', fontStyle: 'italic' },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -33,8 +33,17 @@ const styles = StyleSheet.create({
|
||||
sceneCaption: { fontSize: 7, color: '#64748b', fontStyle: 'italic', textAlign: 'center', marginBottom: 6 },
|
||||
mathCard: { marginTop: 8, padding: 8, backgroundColor: '#f8fafc', borderLeft: '2.5pt solid #6b21a8', borderRadius: 2 },
|
||||
recCard: { marginTop: 8, padding: 8, backgroundColor: '#faf5ff', borderLeft: '2.5pt solid #a855f7', borderRadius: 2 },
|
||||
signatureBlock: { marginTop: 40, flexDirection: 'row', justifyContent: 'space-around', paddingTop: 30 },
|
||||
signatureLine: { width: 200, borderTop: '0.75pt solid #334155', paddingTop: 4, alignItems: 'center' },
|
||||
signatureText: { fontSize: 9, color: '#334155' },
|
||||
});
|
||||
|
||||
export function getEffortColor(val: string | number): string {
|
||||
const num = typeof val === 'number' ? val : parseFloat(val.toString().replace(',', '.').replace(/[^\d.-]/g, ''));
|
||||
if (isNaN(num) || num === 0) return '#1e293b';
|
||||
return num < 0 ? '#ea580c' : '#2563eb'; // orange for negative (suction), blue for positive (compression)
|
||||
}
|
||||
|
||||
export function appendKgf(val: string | number): string {
|
||||
if (typeof val === 'number') return String(val);
|
||||
return val.replace(/(-?\d+(?:[.,]\d+)?)\s*(kN(?:\/m²|\/m2|\/m|·m|\.m|m)?)/g, (match, numStr, unit) => {
|
||||
@@ -136,32 +145,53 @@ const GenericReportDocument = ({ moduleName, sections, wind, sceneImage }: Gener
|
||||
</View>
|
||||
)}
|
||||
<View style={{ flex: 1, marginLeft: 15 }}>
|
||||
<Text style={styles.title}>BrainWind v1.0 — Memória de Cálculo</Text>
|
||||
<Text style={styles.title}>{wind.projectName || 'BrainWind v1.0 — Memória de Cálculo'}</Text>
|
||||
<Text style={styles.subtitle}>Módulo: {moduleName} — ABNT NBR 6123:2023</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginBottom: 12, backgroundColor: '#f8fafc', padding: 6, borderRadius: 2 }}>
|
||||
<View>
|
||||
<Text style={{ fontSize: 8, color: '#64748b' }}>Cliente</Text>
|
||||
<Text style={{ fontSize: 9, fontWeight: 'bold' }}>{wind.clientName || '-'}</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Text style={{ fontSize: 8, color: '#64748b' }}>Autor</Text>
|
||||
<Text style={{ fontSize: 9, fontWeight: 'bold' }}>{wind.authorName || '-'}</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Text style={{ fontSize: 8, color: '#64748b' }}>Revisão</Text>
|
||||
<Text style={{ fontSize: 9, fontWeight: 'bold' }}>{wind.projectRev || '-'}</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Text style={{ fontSize: 8, color: '#64748b' }}>Data</Text>
|
||||
<Text style={{ fontSize: 9, fontWeight: 'bold' }}>{new Date().toLocaleDateString('pt-BR')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 1. Parâmetros Globais do Vento */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>1. Parâmetros Globais do Vento e Pressão Dinâmica</Text>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>Localidade de Projeto:</Text>
|
||||
<Text style={styles.value}>{wind.locality || 'V₀ generalizado'}</Text>
|
||||
<Text style={styles.value}>{wind.locality || 'V0 generalizado'}</Text>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>Velocidade Básica (V₀):</Text>
|
||||
<Text style={styles.label}>Velocidade Básica (V0):</Text>
|
||||
<Text style={styles.value}>{wind.v0} m/s (conforme Figura 1 e Anexo C da NBR 6123)</Text>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>Fator Topográfico (S₁):</Text>
|
||||
<Text style={styles.label}>Fator Topográfico (S1):</Text>
|
||||
<Text style={styles.value}>{wind.s1} (conforme Seção 5.2)</Text>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>Fator de Rugosidade (S₂):</Text>
|
||||
<Text style={styles.value}>{wind.s2.toFixed(3)} (Categoria {wind.terrainCategory}, Classe {wind.structureClass}, conforme Tabela 2)</Text>
|
||||
<Text style={styles.label}>Fator de Rugosidade (S2):</Text>
|
||||
<Text style={styles.value}>
|
||||
{wind.s2.toFixed(3)} (Categoria {wind.terrainCategory}, Classe {wind.structureClass}, conforme Tabela 2)
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>Fator Estatístico (S₃):</Text>
|
||||
<Text style={styles.label}>Fator Estatístico (S3):</Text>
|
||||
<Text style={styles.value}>{wind.s3.toFixed(2)} (Grupo {wind.s3Group}, conforme Tabela 4)</Text>
|
||||
</View>
|
||||
|
||||
@@ -169,17 +199,17 @@ const GenericReportDocument = ({ moduleName, sections, wind, sceneImage }: Gener
|
||||
<Text style={{ fontSize: 10, fontWeight: 'bold', marginBottom: 3, color: '#1e1b4b' }}>
|
||||
Formulações de Velocidade e Pressão (Sec. 4.2 e 4.3):
|
||||
</Text>
|
||||
<Text style={{ fontSize: 9, fontFamily: 'Courier', marginBottom: 2 }}>
|
||||
Vₖ = V₀ × S₁ × S₂ × S₃
|
||||
<Text style={{ fontSize: 9, fontWeight: 'bold', color: '#1e1b4b', marginBottom: 2 }}>
|
||||
Fórmula Base: Vk = V0 × S1 × S2 × S3
|
||||
</Text>
|
||||
<Text style={{ fontSize: 9, fontFamily: 'Courier', marginBottom: 6, color: '#475569' }}>
|
||||
Vₖ = {wind.v0} × {wind.s1} × {wind.s2.toFixed(3)} × {wind.s3.toFixed(2)} = {wind.vk.toFixed(2)} m/s
|
||||
<Text style={{ fontSize: 8, color: '#475569', marginBottom: 6 }}>
|
||||
Vk = {wind.v0} × {wind.s1} × {wind.s2.toFixed(3)} × {wind.s3.toFixed(2)} = {wind.vk.toFixed(2)} m/s
|
||||
</Text>
|
||||
<Text style={{ fontSize: 9, fontFamily: 'Courier', marginBottom: 2 }}>
|
||||
q = 0,613 × (Vₖ)²
|
||||
<Text style={{ fontSize: 9, fontWeight: 'bold', color: '#1e1b4b', marginBottom: 2 }}>
|
||||
Pressão Dinâmica: q = 0,613 × (Vk)²
|
||||
</Text>
|
||||
<Text style={{ fontSize: 9, fontFamily: 'Courier', color: '#475569' }}>
|
||||
{appendKgf(`q = 0,613 × (${wind.vk.toFixed(2)})² = ${(0.613 * Math.pow(wind.vk, 2) / 1000).toFixed(4)} kN/m²`)}
|
||||
<Text style={{ fontSize: 8, color: '#475569' }}>
|
||||
q = 0,613 × ({wind.vk.toFixed(2)})² = {(0.613 * Math.pow(wind.vk, 2) / 1000).toFixed(4)} kN/m²
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -208,7 +238,9 @@ const GenericReportDocument = ({ moduleName, sections, wind, sceneImage }: Gener
|
||||
<View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
|
||||
{sec.gridItems.map((item, i) => (
|
||||
<View style={{ width: '50%', marginBottom: 4 }} key={i}>
|
||||
<Text style={{ fontWeight: 'bold', color: '#475569' }}>{item.label}:</Text>
|
||||
<Text style={{ fontWeight: 'bold', color: '#475569' }}>
|
||||
{item.label.replace('⊥', 'perpendicular à').replace('∥', 'paralelo à')}:
|
||||
</Text>
|
||||
<Text style={{ color: '#1e293b' }}>{appendKgf(item.value)}</Text>
|
||||
</View>
|
||||
))}
|
||||
@@ -227,7 +259,7 @@ const GenericReportDocument = ({ moduleName, sections, wind, sceneImage }: Gener
|
||||
{sec.tableRows.map((tr, rIdx) => (
|
||||
<View style={styles.tableRow} key={rIdx}>
|
||||
{tr.map((tc, cIdx) => (
|
||||
<Text key={cIdx} style={cIdx === 0 ? styles.tableCellFirst : styles.tableCell}>
|
||||
<Text key={cIdx} style={[cIdx === 0 ? styles.tableCellFirst : styles.tableCell, cIdx > 0 ? { color: getEffortColor(tc) } : {}]}>
|
||||
{appendKgf(tc)}
|
||||
</Text>
|
||||
))}
|
||||
@@ -238,7 +270,6 @@ const GenericReportDocument = ({ moduleName, sections, wind, sceneImage }: Gener
|
||||
</View>
|
||||
))}
|
||||
|
||||
{/* 6. Parecer Técnico e Considerações */}
|
||||
<View style={styles.section} wrap={false}>
|
||||
<Text style={styles.sectionTitle}>
|
||||
{sceneImage ? sections.length + 3 : sections.length + 2}. Parecer Técnico e Observações
|
||||
@@ -250,28 +281,38 @@ const GenericReportDocument = ({ moduleName, sections, wind, sceneImage }: Gener
|
||||
</Text>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<View style={styles.signatureBlock}>
|
||||
<View style={styles.signatureLine}>
|
||||
<Text style={styles.signatureText}>{wind.authorName || 'Engenheiro Responsável'}</Text>
|
||||
<Text style={{ fontSize: 7, color: '#64748b', marginTop: 2 }}>CREA / CAU:</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={{ marginTop: 20, paddingTop: 10, borderTop: '0.5pt solid #e2e8f0' }}>
|
||||
<Text style={{ fontSize: 6, color: '#94a3b8', textAlign: 'justify', lineHeight: 1.3 }}>
|
||||
Isenção de Responsabilidade do Software: Os resultados e relatórios gerados pelo BrainWind são de responsabilidade exclusiva e integral do usuário. O aplicativo atua como ferramenta de auxílio e não substitui o julgamento técnico, a análise detalhada das normas e o dimensionamento final por profissional habilitado.
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Rodapé fixo */}
|
||||
<View style={styles.footer} fixed>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Text style={{ fontSize: 6.5, color: '#94a3b8', flex: 1, textAlign: 'left' }}>
|
||||
Legendas: V₀: Vel. básica | S₁, S₂, S₃: Fatores de vento | Vₖ: Vel. de projeto | q: Pressão dinâmica | Cpe: Pressão externa | Cpi: Pressão interna
|
||||
</Text>
|
||||
<Text style={{ fontSize: 7, color: '#64748b', textAlign: 'center', marginHorizontal: 10 }} render={({ pageNumber, totalPages }) => (
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text style={{ fontSize: 6, color: '#94a3b8' }}>
|
||||
Legendas: V0: Vel. básica | S1, S2, S3: Fatores de vento | Vk: Vel. de projeto | q: Pressão dinâmica | Cpe: Pressão externa | Cpi: Pressão interna
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={{ fontSize: 7, color: '#64748b', textAlign: 'right', flex: 0.2, paddingRight: 10 }} render={({ pageNumber, totalPages }) => (
|
||||
`Pág. ${pageNumber} de ${totalPages}`
|
||||
)} />
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', flex: 0.3 }}>
|
||||
<Svg width="8" height="8" viewBox="0 0 24 24" fill="none" stroke="#6b21a8" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" style={{ marginRight: 2 }}>
|
||||
<Path d="M12.8 19.6A2 2 0 1 0 14 16H2" />
|
||||
<Path d="M17.5 8a2.5 2.5 0 1 1 2 4H2" />
|
||||
<Path d="M9.8 4.4A2 2 0 1 1 11 8H2" />
|
||||
</Svg>
|
||||
<Text style={{ fontSize: 7, fontWeight: 'bold', color: '#6b21a8' }}>BrainWind</Text>
|
||||
<PdfImage src="/logo_brainwind.png" style={{ width: 25, height: 'auto', objectFit: 'contain' }} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Page>
|
||||
|
||||
</Document>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user