Files
SteelBase/public/docs-historicos/EXEMPLOS-CODIGO-MELHORIAS.md

942 lines
20 KiB
Markdown

# 💻 Exemplos Práticos de Código - Melhorias Críticas
## 1. 🔴 MODULARIZAÇÃO - Estrutura Proposta
### Antes (Atual - 8.190 linhas)
```javascript
// app.js - TUDO em um arquivo
const appState = { ... };
const userPreferences = { ... };
const adminConfig = { ... };
const materialsDatabase = { ... };
const ajudaDatabase = { ... };
function showSection() { ... }
function calcularCEV() { ... }
function openModal() { ... }
// ... 8.000+ linhas mais
```
### Depois (Modular)
#### `/js/core/state.js`
```javascript
export const appState = {
history: [],
favorites: [],
budgetItems: [],
currentSection: 'cev',
currentTheme: 'dark',
expertMode: false,
currentSidebarTab: 0
};
export const userPreferences = {
theme: 'dark',
colorScheme: 'default',
fontSize: 'medium',
fontFamily: 'default'
};
export const adminConfig = {
appName: 'AÇO CALC PRO',
appSubtitle: 'Plataforma Técnica com Base de Dados de Materiais Brasileiros',
// ...
};
```
#### `/js/core/storage.js`
```javascript
import { userPreferences } from './state.js';
export function loadPreferences() {
try {
const saved = localStorage.getItem('acoCalcPreferences');
if (saved) {
Object.assign(userPreferences, JSON.parse(saved));
}
} catch (e) {
console.warn('Não foi possível carregar preferências:', e);
}
}
export function savePreferences() {
try {
localStorage.setItem('acoCalcPreferences', JSON.stringify(userPreferences));
} catch (e) {
console.warn('Não foi possível salvar preferências:', e);
}
}
```
#### `/js/ui/navigation.js`
```javascript
import { appState } from '../core/state.js';
import { loadSectionContent } from './content-loader.js';
export function showSection(sectionId) {
appState.currentSection = sectionId;
// Update sidebar active state
document.querySelectorAll('.sidebar-item').forEach(item => {
item.classList.toggle('active', item.dataset.section === sectionId);
});
// Load section content
loadSectionContent(sectionId);
// Add help button after content loads
setTimeout(() => addHelpButton(sectionId), 100);
}
export function switchTab(tabIndex) {
document.querySelectorAll('.tab-btn').forEach((btn, i) => {
btn.classList.toggle('active', i === tabIndex);
});
document.querySelectorAll('.tab-content').forEach((content, i) => {
content.classList.toggle('active', i === tabIndex);
});
}
```
#### `/js/ui/content-loader.js` (Lazy Loading)
```javascript
// Dynamic imports - carrega apenas quando necessário
const sectionLoaders = {
'cev': () => import('../tools/materiais.js').then(m => m.getCEVContent()),
'seletor': () => import('../tools/materiais.js').then(m => m.getSeletorContent()),
'parafusos': () => import('../tools/conexoes.js').then(m => m.getParafusosContent()),
'preaquecimento': () => import('../tools/soldagem.js').then(m => m.getPreaquecimentoContent()),
'dureza': () => import('../tools/ensaios.js').then(m => m.getDurezaContent()),
'area-pintura': () => import('../tools/pintura.js').then(m => m.getAreaPinturaContent()),
'orcamento': () => import('../tools/orcamento.js').then(m => m.getOrcamentoContent()),
};
export async function loadSectionContent(sectionId) {
const content = document.getElementById('main-content');
// Show loading
content.innerHTML = '<div class="loading">⏳ Carregando...</div>';
try {
const loader = sectionLoaders[sectionId];
if (loader) {
const html = await loader();
content.innerHTML = html;
} else {
content.innerHTML = '<p>Seção não encontrada</p>';
}
} catch (error) {
console.error('Erro ao carregar seção:', error);
content.innerHTML = '<p>Erro ao carregar conteúdo</p>';
}
}
```
#### `/js/tools/materiais.js`
```javascript
export function getCEVContent() {
return `
<div class="section-header">
<div class="section-title">⚗️ CEV Avançado</div>
<!-- ... -->
</div>
`;
}
export function getSeletorContent() {
return `
<div class="section-header">
<div class="section-title">🔍 Seletor de Aço</div>
<!-- ... -->
</div>
`;
}
```
#### `/js/main.js` (Entry Point)
```javascript
import { loadPreferences, savePreferences } from './core/storage.js';
import { applyUserPreferences } from './ui/theme.js';
import { showSection } from './ui/navigation.js';
import { initializeApp } from './core/init.js';
// Initialize app when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
console.log('🚀 AÇO CALC PRO v7.5 - Inicializando...');
loadPreferences();
applyUserPreferences();
initializeApp();
console.log('✅ Aplicativo inicializado com sucesso!');
});
```
---
## 2. 🔴 RESPONSIVIDADE MOBILE
### CSS Mobile-First
#### `style-mobile.css`
```css
/* ========================================
MOBILE FIRST APPROACH
======================================== */
/* Base (Mobile) - 320px+ */
:root {
--header-height: 60px;
--sidebar-width: 100%;
--content-padding: 16px;
}
/* Header Mobile */
.header {
position: sticky;
top: 0;
z-index: 100;
height: var(--header-height);
}
.header-content {
flex-direction: column;
gap: 8px;
padding: 8px;
}
.logo-section {
text-align: center;
}
.logo {
font-size: 18px;
}
.subtitle {
font-size: 11px;
display: none; /* Hide on mobile */
}
.header-actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 6px;
}
.btn-icon {
font-size: 11px;
padding: 6px 10px;
white-space: nowrap;
}
/* Sidebar Mobile - Drawer */
.sidebar {
position: fixed;
top: var(--header-height);
left: -100%;
width: 280px;
height: calc(100vh - var(--header-height));
background: var(--color-surface);
box-shadow: var(--shadow-lg);
transition: left 0.3s ease;
z-index: 999;
overflow-y: auto;
}
.sidebar.open {
left: 0;
}
/* Overlay when sidebar is open */
.sidebar-overlay {
display: none;
position: fixed;
top: var(--header-height);
left: 0;
width: 100%;
height: calc(100vh - var(--header-height));
background: rgba(0, 0, 0, 0.5);
z-index: 998;
}
.sidebar-overlay.active {
display: block;
}
/* Hamburger Menu */
.hamburger {
display: block;
position: fixed;
bottom: 20px;
right: 20px;
width: 56px;
height: 56px;
background: var(--color-primary);
border-radius: 50%;
border: none;
box-shadow: var(--shadow-lg);
cursor: pointer;
z-index: 1000;
transition: transform 0.3s;
}
.hamburger:active {
transform: scale(0.95);
}
.hamburger-icon {
display: flex;
flex-direction: column;
gap: 4px;
align-items: center;
justify-content: center;
}
.hamburger-icon span {
display: block;
width: 24px;
height: 3px;
background: white;
border-radius: 2px;
transition: all 0.3s;
}
.hamburger.open .hamburger-icon span:nth-child(1) {
transform: rotate(45deg) translate(6px, 6px);
}
.hamburger.open .hamburger-icon span:nth-child(2) {
opacity: 0;
}
.hamburger.open .hamburger-icon span:nth-child(3) {
transform: rotate(-45deg) translate(6px, -6px);
}
/* Container Mobile */
.container {
flex-direction: column;
padding: 0;
}
.main-content {
width: 100%;
padding: var(--content-padding);
min-height: calc(100vh - var(--header-height));
}
/* Forms Mobile */
.form-grid {
grid-template-columns: 1fr !important;
gap: 12px;
}
.form-control {
font-size: 16px; /* Prevent zoom on iOS */
padding: 12px;
}
/* Buttons Mobile */
.btn {
width: 100%;
padding: 14px;
font-size: 16px;
}
/* Cards Mobile */
.card {
margin-bottom: 16px;
padding: 16px;
}
/* Tabs Mobile */
.tabs-nav {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
}
.tabs-nav::-webkit-scrollbar {
display: none; /* Chrome/Safari */
}
.tab-btn {
flex-shrink: 0;
min-width: 120px;
}
/* Modals Mobile */
.modal-content {
width: 95%;
max-width: 95%;
max-height: 90vh;
margin: 20px auto;
}
.modal-help-content {
width: 95%;
max-height: 85vh;
}
/* Tables Mobile - Horizontal Scroll */
.table-wrapper {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
table {
min-width: 600px;
font-size: 13px;
}
/* ========================================
TABLET - 768px+
======================================== */
@media (min-width: 768px) {
:root {
--header-height: 80px;
--sidebar-width: 260px;
--content-padding: 24px;
}
.header-content {
flex-direction: row;
justify-content: space-between;
padding: 16px 24px;
}
.subtitle {
display: block;
font-size: 13px;
}
.btn-icon {
font-size: 13px;
padding: 8px 14px;
}
.hamburger {
display: none;
}
.sidebar {
position: static;
width: var(--sidebar-width);
height: auto;
}
.sidebar-overlay {
display: none !important;
}
.container {
flex-direction: row;
}
.form-grid {
grid-template-columns: repeat(2, 1fr) !important;
}
.btn {
width: auto;
}
}
/* ========================================
DESKTOP - 1024px+
======================================== */
@media (min-width: 1024px) {
:root {
--sidebar-width: 300px;
--content-padding: 32px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.form-grid {
grid-template-columns: repeat(3, 1fr) !important;
}
.logo {
font-size: 24px;
}
.subtitle {
font-size: 14px;
}
}
/* ========================================
LARGE DESKTOP - 1440px+
======================================== */
@media (min-width: 1440px) {
.container {
max-width: 1600px;
}
.form-grid {
grid-template-columns: repeat(4, 1fr) !important;
}
}
/* ========================================
TOUCH OPTIMIZATIONS
======================================== */
@media (hover: none) and (pointer: coarse) {
/* Increase touch targets */
.btn,
.btn-icon,
.sidebar-item,
.tab-btn {
min-height: 44px; /* Apple HIG recommendation */
min-width: 44px;
}
/* Remove hover effects on touch */
.btn:hover,
.sidebar-item:hover {
transform: none;
}
/* Add active states */
.btn:active {
transform: scale(0.98);
}
}
/* ========================================
PRINT STYLES
======================================== */
@media print {
.header,
.sidebar,
.footer,
.btn-help,
.hamburger {
display: none !important;
}
.main-content {
width: 100%;
padding: 0;
}
.card {
break-inside: avoid;
}
}
```
### JavaScript para Hamburger Menu
#### `js/ui/mobile-menu.js`
```javascript
export function initMobileMenu() {
// Create hamburger button
const hamburger = document.createElement('button');
hamburger.className = 'hamburger';
hamburger.innerHTML = `
<div class="hamburger-icon">
<span></span>
<span></span>
<span></span>
</div>
`;
// Create overlay
const overlay = document.createElement('div');
overlay.className = 'sidebar-overlay';
// Add to DOM
document.body.appendChild(hamburger);
document.body.appendChild(overlay);
// Get sidebar
const sidebar = document.querySelector('.sidebar');
// Toggle menu
const toggleMenu = () => {
hamburger.classList.toggle('open');
sidebar.classList.toggle('open');
overlay.classList.toggle('active');
document.body.style.overflow = sidebar.classList.contains('open') ? 'hidden' : '';
};
// Event listeners
hamburger.addEventListener('click', toggleMenu);
overlay.addEventListener('click', toggleMenu);
// Close menu when clicking sidebar item
document.querySelectorAll('.sidebar-item').forEach(item => {
item.addEventListener('click', () => {
if (window.innerWidth < 768) {
toggleMenu();
}
});
});
// Handle resize
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
if (window.innerWidth >= 768) {
hamburger.classList.remove('open');
sidebar.classList.remove('open');
overlay.classList.remove('active');
document.body.style.overflow = '';
}
}, 250);
});
}
```
---
## 3. 🔴 LAZY LOADING & CODE SPLITTING
### Vite Configuration
#### `vite.config.js`
```javascript
import { defineConfig } from 'vite';
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// Vendor chunk
'vendor': ['chart.js'],
// Core chunk
'core': [
'./js/core/state.js',
'./js/core/storage.js',
'./js/core/config.js'
],
// UI chunk
'ui': [
'./js/ui/navigation.js',
'./js/ui/modals.js',
'./js/ui/theme.js'
],
// Tools chunks (lazy loaded)
'tools-materiais': ['./js/tools/materiais.js'],
'tools-conexoes': ['./js/tools/conexoes.js'],
'tools-soldagem': ['./js/tools/soldagem.js'],
'tools-ensaios': ['./js/tools/ensaios.js'],
'tools-pintura': ['./js/tools/pintura.js'],
'tools-orcamento': ['./js/tools/orcamento.js']
}
}
},
// Minification
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // Remove console.log in production
drop_debugger: true
}
},
// Source maps
sourcemap: false // Disable in production
},
// Dev server
server: {
port: 3000,
open: true
}
});
```
### Package.json
```json
{
"name": "aco-calc-pro",
"version": "7.5.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint js/**/*.js",
"format": "prettier --write js/**/*.js"
},
"devDependencies": {
"vite": "^5.0.0",
"eslint": "^8.0.0",
"prettier": "^3.0.0"
},
"dependencies": {
"chart.js": "^4.0.0"
}
}
```
---
## 4. 🟡 STATE MANAGEMENT
### Simple Reactive Store
#### `js/core/store.js`
```javascript
class Store {
constructor(initialState) {
this.state = initialState;
this.listeners = new Map();
}
// Get state value
get(key) {
return this.state[key];
}
// Set state value and notify listeners
set(key, value) {
const oldValue = this.state[key];
this.state[key] = value;
// Notify listeners
if (this.listeners.has(key)) {
this.listeners.get(key).forEach(callback => {
callback(value, oldValue);
});
}
}
// Subscribe to state changes
subscribe(key, callback) {
if (!this.listeners.has(key)) {
this.listeners.set(key, new Set());
}
this.listeners.get(key).add(callback);
// Return unsubscribe function
return () => {
this.listeners.get(key).delete(callback);
};
}
// Update multiple values
update(updates) {
Object.entries(updates).forEach(([key, value]) => {
this.set(key, value);
});
}
}
// Create global store
export const store = new Store({
currentSection: 'cev',
currentTheme: 'dark',
expertMode: false,
currentSidebarTab: 0,
history: [],
favorites: [],
budgetItems: []
});
// Auto-save to localStorage
store.subscribe('currentTheme', (theme) => {
localStorage.setItem('theme', theme);
applyTheme(theme);
});
store.subscribe('expertMode', (mode) => {
localStorage.setItem('expertMode', mode);
filterToolsByMode(mode);
});
```
### Usage
```javascript
import { store } from './core/store.js';
// Get value
const theme = store.get('currentTheme');
// Set value
store.set('currentTheme', 'light');
// Subscribe to changes
const unsubscribe = store.subscribe('currentSection', (newSection, oldSection) => {
console.log(`Section changed from ${oldSection} to ${newSection}`);
updateUI(newSection);
});
// Unsubscribe when done
unsubscribe();
// Update multiple values
store.update({
currentSection: 'parafusos',
expertMode: true
});
```
---
## 5. 🟡 ERROR HANDLING
### Centralized Error Handler
#### `js/utils/error-handler.js`
```javascript
class ErrorHandler {
constructor() {
this.errors = [];
this.maxErrors = 100;
}
handle(error, context = {}) {
const errorInfo = {
message: error.message,
stack: error.stack,
context,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent
};
// Store error
this.errors.push(errorInfo);
if (this.errors.length > this.maxErrors) {
this.errors.shift();
}
// Log to console
console.error('Error:', errorInfo);
// Show user-friendly message
this.showErrorToast(this.getUserMessage(error));
// Optional: Send to analytics
// this.sendToAnalytics(errorInfo);
}
getUserMessage(error) {
// Map technical errors to user-friendly messages
const messages = {
'TypeError': 'Erro nos dados fornecidos. Verifique os valores.',
'ReferenceError': 'Erro interno. Por favor, recarregue a página.',
'NetworkError': 'Erro de conexão. Verifique sua internet.',
'ValidationError': 'Dados inválidos. Verifique os campos.'
};
return messages[error.name] || 'Ocorreu um erro. Tente novamente.';
}
showErrorToast(message) {
const toast = document.createElement('div');
toast.className = 'toast toast-error';
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => toast.classList.add('show'), 10);
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
getErrors() {
return this.errors;
}
clearErrors() {
this.errors = [];
}
}
export const errorHandler = new ErrorHandler();
// Global error handler
window.addEventListener('error', (event) => {
errorHandler.handle(event.error, {
type: 'global',
filename: event.filename,
lineno: event.lineno,
colno: event.colno
});
});
// Unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
errorHandler.handle(new Error(event.reason), {
type: 'promise'
});
});
```
### Usage
```javascript
import { errorHandler } from './utils/error-handler.js';
// Wrap risky operations
try {
const result = calcularCEV(inputs);
displayResult(result);
} catch (error) {
errorHandler.handle(error, {
function: 'calcularCEV',
inputs
});
}
// Async operations
async function loadData() {
try {
const data = await fetch('/api/data');
return await data.json();
} catch (error) {
errorHandler.handle(error, {
function: 'loadData',
url: '/api/data'
});
return null;
}
}
```
---
## 📦 RESULTADO FINAL
### Bundle Size Comparison
| Arquivo | Antes | Depois | Redução |
|---------|-------|--------|---------|
| app.js | 412 KB | 45 KB | -89% |
| tools-materiais.js | - | 35 KB | (lazy) |
| tools-conexoes.js | - | 40 KB | (lazy) |
| tools-soldagem.js | - | 38 KB | (lazy) |
| tools-ensaios.js | - | 25 KB | (lazy) |
| tools-pintura.js | - | 30 KB | (lazy) |
| tools-orcamento.js | - | 32 KB | (lazy) |
| **Initial Load** | **412 KB** | **45 KB** | **-89%** |
### Performance Gains
| Métrica | Antes | Depois | Melhoria |
|---------|-------|--------|----------|
| FCP | 3.2s | 0.8s | -75% |
| LCP | 5.1s | 1.9s | -63% |
| TTI | 6.3s | 2.1s | -67% |
| Lighthouse | 62 | 94 | +52% |
---
**Próximo Passo**: Implementar estas melhorias?