feat: add direct db injection button to Bocad Converter

This commit is contained in:
2026-09-29 10:10:47 -03:00
parent e537d731f3
commit 8755852fbd
+86 -2
View File
@@ -24,6 +24,7 @@ import {
} from 'lucide-react'; } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import * as XLSX from 'xlsx'; import * as XLSX from 'xlsx';
import { usePecas } from '@/hooks/usePecas';
export interface BocadConvertedPiece { export interface BocadConvertedPiece {
of: string; of: string;
@@ -39,6 +40,7 @@ export interface BocadConvertedPiece {
perfilPrincipal: string; perfilPrincipal: string;
comprimentoRef: number | string; comprimentoRef: number | string;
qtdComponentesFilhos: number; qtdComponentesFilhos: number;
components?: RawBocadComponent[];
} }
interface RawBocadComponent { interface RawBocadComponent {
@@ -66,8 +68,10 @@ interface RawBocadAssembly {
} }
export const BocadConverter: React.FC = () => { export const BocadConverter: React.FC = () => {
const { importPecas } = usePecas();
const [selectedFile, setSelectedFile] = useState<File | null>(null); const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [isProcessing, setIsProcessing] = useState(false); const [isProcessing, setIsProcessing] = useState(false);
const [isDirectImporting, setIsDirectImporting] = useState(false);
const [tratamentoGlobal, setTratamentoGlobal] = useState<'pintura' | 'galvanizacao'>('pintura'); const [tratamentoGlobal, setTratamentoGlobal] = useState<'pintura' | 'galvanizacao'>('pintura');
const [extractedPieces, setExtractedPieces] = useState<BocadConvertedPiece[]>([]); const [extractedPieces, setExtractedPieces] = useState<BocadConvertedPiece[]>([]);
const [headerOf, setHeaderOf] = useState<string>(''); const [headerOf, setHeaderOf] = useState<string>('');
@@ -410,7 +414,8 @@ export const BocadConverter: React.FC = () => {
material: normalizedMat, material: normalizedMat,
perfilPrincipal: perfilPrincipal, perfilPrincipal: perfilPrincipal,
comprimentoRef: compRef, comprimentoRef: compRef,
qtdComponentesFilhos: whiteRowsCount qtdComponentesFilhos: whiteRowsCount,
components: asm.components
}; };
}); });
@@ -515,6 +520,66 @@ export const BocadConverter: React.FC = () => {
} }
}; };
const handleDirectImport = async () => {
if (extractedPieces.length === 0) return;
setIsDirectImporting(true);
try {
const importacaoRows: any[] = [];
extractedPieces.forEach((item) => {
if (item.compostoPorComponentes === 'SIM' && item.components && item.components.length > 0) {
item.components.forEach((comp) => {
importacaoRows.push({
of_number: item.of,
etapa_fase: item.fase,
marca: item.marca,
descricao: item.descricao,
quantidade: Number(item.quantidade),
peso_unitario: item.pesoUnitario || 0,
peso_total: item.pesoTotal || 0,
tratamento_superficial: item.tratamentoSuperficial,
material: item.material,
perfil_principal: item.perfilPrincipal,
tem_componentes: true,
marca_componente: comp.marca || '',
descricao_componente: comp.perfil || item.descricao,
perfil_componente: comp.perfil || item.descricao,
peso_unitario_componente: comp.pesoUnit || 0,
quantidade_por_peca: Number(comp.quant) || 1
});
});
} else {
importacaoRows.push({
of_number: item.of,
etapa_fase: item.fase,
marca: item.marca,
descricao: item.descricao,
quantidade: Number(item.quantidade),
peso_unitario: item.pesoUnitario || 0,
peso_total: item.pesoTotal || 0,
tratamento_superficial: item.tratamentoSuperficial,
material: item.material,
perfil_principal: item.perfilPrincipal,
tem_componentes: false,
marca_componente: '',
descricao_componente: '',
perfil_componente: '',
peso_unitario_componente: 0,
quantidade_por_peca: 0
});
}
});
await importPecas(importacaoRows);
toast.success(`${extractedPieces.length} peças foram importadas com sucesso!`);
addLog(`✅ Importação concluída: ${extractedPieces.length} peças gravadas no banco.`);
} catch (err: any) {
console.error('Erro na importação direta:', err);
toast.error(`Falha ao importar: ${err?.message || 'Erro no banco'}`);
} finally {
setIsDirectImporting(false);
}
};
const totalPesoGeral = extractedPieces.reduce((acc, curr) => acc + (curr.pesoTotal || 0), 0); const totalPesoGeral = extractedPieces.reduce((acc, curr) => acc + (curr.pesoTotal || 0), 0);
const totalPecasCompostas = extractedPieces.filter((p) => p.compostoPorComponentes === 'SIM').length; const totalPecasCompostas = extractedPieces.filter((p) => p.compostoPorComponentes === 'SIM').length;
const totalPecasSimples = extractedPieces.filter((p) => p.compostoPorComponentes === 'NAO').length; const totalPecasSimples = extractedPieces.filter((p) => p.compostoPorComponentes === 'NAO').length;
@@ -689,11 +754,30 @@ export const BocadConverter: React.FC = () => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button <Button
onClick={exportToOfficialExcel} onClick={exportToOfficialExcel}
className="bg-emerald-600 hover:bg-emerald-500 text-white font-semibold text-xs px-4 h-9 shadow-lg shadow-emerald-950/40" variant="outline"
className="border-emerald-600/60 text-emerald-400 hover:bg-emerald-600/10 font-semibold text-xs px-3.5 h-9"
> >
<Download className="w-4 h-4 mr-1.5" /> <Download className="w-4 h-4 mr-1.5" />
Baixar Planilha Padrão (.xlsx) Baixar Planilha Padrão (.xlsx)
</Button> </Button>
<Button
onClick={handleDirectImport}
disabled={isDirectImporting}
className="bg-indigo-600 hover:bg-indigo-500 text-white font-semibold text-xs px-4 h-9 shadow-lg shadow-indigo-950/40"
>
{isDirectImporting ? (
<>
<RefreshCw className="w-4 h-4 mr-1.5 animate-spin" />
Gravando no Banco...
</>
) : (
<>
<Upload className="w-4 h-4 mr-1.5" />
cadastrar peças na OF
</>
)}
</Button>
</div> </div>
</div> </div>
)} )}