231 lines
6.4 KiB
JavaScript
231 lines
6.4 KiB
JavaScript
/**
|
||
* CSV Manager - CRUD operations for CSV files
|
||
* Handles reading, parsing, editing, and saving CSV data
|
||
*/
|
||
|
||
/**
|
||
* Parse CSV text to array of objects
|
||
* @param {string} csvText - CSV content
|
||
* @returns {Array<object>} Parsed data
|
||
*/
|
||
export function parseCSV(csvText) {
|
||
const lines = csvText.trim().split('\n');
|
||
if (lines.length === 0) return [];
|
||
|
||
// Get headers
|
||
const headers = lines[0].split(',').map(h => h.trim());
|
||
|
||
// Parse rows
|
||
const data = [];
|
||
for (let i = 1; i < lines.length; i++) {
|
||
const values = lines[i].split(',').map(v => v.trim());
|
||
const row = {};
|
||
headers.forEach((header, index) => {
|
||
row[header] = values[index] || '';
|
||
});
|
||
data.push(row);
|
||
}
|
||
|
||
return data;
|
||
}
|
||
|
||
/**
|
||
* Convert array of objects to CSV text
|
||
* @param {Array<object>} data - Data array
|
||
* @returns {string} CSV text
|
||
*/
|
||
export function toCSV(data) {
|
||
if (data.length === 0) return '';
|
||
|
||
// Get headers from first object
|
||
const headers = Object.keys(data[0]);
|
||
|
||
// Create CSV lines
|
||
const lines = [headers.join(',')];
|
||
|
||
data.forEach(row => {
|
||
const values = headers.map(header => {
|
||
const value = row[header] || '';
|
||
// Escape commas and quotes
|
||
if (value.includes(',') || value.includes('"')) {
|
||
return `"${value.replace(/"/g, '""')}"`;
|
||
}
|
||
return value;
|
||
});
|
||
lines.push(values.join(','));
|
||
});
|
||
|
||
return lines.join('\n');
|
||
}
|
||
|
||
/**
|
||
* Load CSV file
|
||
* @param {string} filename - CSV filename
|
||
* @returns {Promise<Array<object>>} Parsed data
|
||
*/
|
||
export async function loadCSV(filename) {
|
||
try {
|
||
const response = await fetch(`BD/${filename}`);
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
const text = await response.text();
|
||
return parseCSV(text);
|
||
} catch (error) {
|
||
console.error(`Erro ao carregar ${filename}:`, error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Download CSV file
|
||
* @param {string} filename - Filename
|
||
* @param {string} csvText - CSV content
|
||
*/
|
||
export function downloadCSV(filename, csvText) {
|
||
const blob = new Blob([csvText], { type: 'text/csv;charset=utf-8;' });
|
||
const link = document.createElement('a');
|
||
const url = URL.createObjectURL(blob);
|
||
|
||
link.setAttribute('href', url);
|
||
link.setAttribute('download', filename);
|
||
link.style.visibility = 'hidden';
|
||
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
/**
|
||
* Get available CSV files
|
||
* @returns {Array<object>} List of CSV files with metadata
|
||
*/
|
||
export function getAvailableCSVFiles() {
|
||
return [
|
||
{
|
||
id: 'perfis_w',
|
||
name: 'Perfis W',
|
||
filename: 'perfis_w.csv',
|
||
description: 'Perfis de aço tipo W (vigas)',
|
||
icon: '🏗️'
|
||
},
|
||
{
|
||
id: 'perfis_i',
|
||
name: 'Perfis I',
|
||
filename: 'perfis_i.csv',
|
||
description: 'Perfis de aço tipo I',
|
||
icon: '🏗️'
|
||
},
|
||
{
|
||
id: 'cantoneiras',
|
||
name: 'Cantoneiras',
|
||
filename: 'cantoneiras.csv',
|
||
description: 'Cantoneiras de aço',
|
||
icon: '📐'
|
||
},
|
||
{
|
||
id: 'tubos_circulares',
|
||
name: 'Tubos Circulares',
|
||
filename: 'tubos_circulares.csv',
|
||
description: 'Tubos de seção circular',
|
||
icon: '⭕'
|
||
},
|
||
{
|
||
id: 'tubos_rhs',
|
||
name: 'Tubos RHS',
|
||
filename: 'tubos_rhs.csv',
|
||
description: 'Tubos retangulares/quadrados',
|
||
icon: '⬜'
|
||
},
|
||
{
|
||
id: 'chapas',
|
||
name: 'Chapas',
|
||
filename: 'chapas.csv',
|
||
description: 'Chapas de aço',
|
||
icon: '📄'
|
||
},
|
||
{
|
||
id: 'barras',
|
||
name: 'Barras',
|
||
filename: 'barras.csv',
|
||
description: 'Barras redondas',
|
||
icon: '➖'
|
||
},
|
||
{
|
||
id: 'eletrodos',
|
||
name: 'Eletrodos',
|
||
filename: 'eletrodos.csv',
|
||
description: 'Eletrodos de soldagem',
|
||
icon: '⚡'
|
||
},
|
||
{
|
||
id: 'parafusos',
|
||
name: 'Parafusos',
|
||
filename: 'parafusos.csv',
|
||
description: 'Parafusos estruturais',
|
||
icon: '🔩'
|
||
},
|
||
{
|
||
id: 'tintas',
|
||
name: 'Tintas',
|
||
filename: 'tintas.csv',
|
||
description: 'Tintas e revestimentos',
|
||
icon: '🎨'
|
||
},
|
||
{
|
||
id: 'acos_soldagem',
|
||
name: 'Aços - Soldagem',
|
||
filename: 'Tabela_Acos_Soldagem_Consumiveis.csv',
|
||
description: 'Relação aços e consumíveis',
|
||
icon: '🔥'
|
||
},
|
||
{
|
||
id: 'acos_pintura',
|
||
name: 'Aços - Pintura',
|
||
filename: 'Tabela_Acos_Pintura_Tintas.csv',
|
||
description: 'Relação aços e tintas',
|
||
icon: '🎨'
|
||
}
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Validate CSV data
|
||
* @param {Array<object>} data - Data to validate
|
||
* @returns {object} Validation result
|
||
*/
|
||
export function validateCSVData(data) {
|
||
const errors = [];
|
||
|
||
if (!Array.isArray(data) || data.length === 0) {
|
||
errors.push('Dados vazios ou inválidos');
|
||
return { valid: false, errors };
|
||
}
|
||
|
||
// Check if all rows have same keys
|
||
const firstKeys = Object.keys(data[0]).sort();
|
||
for (let i = 1; i < data.length; i++) {
|
||
const keys = Object.keys(data[i]).sort();
|
||
if (JSON.stringify(keys) !== JSON.stringify(firstKeys)) {
|
||
errors.push(`Linha ${i + 1}: Colunas inconsistentes`);
|
||
}
|
||
}
|
||
|
||
// Check for empty required fields (id, nome)
|
||
data.forEach((row, index) => {
|
||
if (!row.id || row.id.trim() === '') {
|
||
errors.push(`Linha ${index + 2}: Campo 'id' vazio`);
|
||
}
|
||
if (!row.nome || row.nome.trim() === '') {
|
||
errors.push(`Linha ${index + 2}: Campo 'nome' vazio`);
|
||
}
|
||
});
|
||
|
||
return {
|
||
valid: errors.length === 0,
|
||
errors
|
||
};
|
||
}
|