chore: ignore test scripts and ifc files, update 3D viewer

This commit is contained in:
2026-09-20 23:04:06 +00:00
parent beda3cc7a9
commit cc79a925a3
4 changed files with 94 additions and 79 deletions
+5
View File
@@ -46,3 +46,8 @@ scripts/auto-synchome.ps1
# Arquivos temporários # Arquivos temporários
*.tmp *.tmp
*.temp *.temp
# Arquivos de teste e dados temporários
test_*.js
test_*.cjs
*.ifc
+46 -35
View File
@@ -13,6 +13,7 @@ interface ProductionPieceStatus {
pointedQtd: number; pointedQtd: number;
currentProcessName?: string; currentProcessName?: string;
processColor?: string; processColor?: string;
processOrdem?: number;
} }
interface ModelViewer3DProps { interface ModelViewer3DProps {
@@ -252,27 +253,46 @@ export const ModelViewer3D: React.FC<ModelViewer3DProps> = ({
const cleanAssMark = String(mesh.userData.cleanAssemblyMark || cleanMark).trim().toUpperCase(); const cleanAssMark = String(mesh.userData.cleanAssemblyMark || cleanMark).trim().toUpperCase();
const section = String(mesh.userData.section || '').trim().toUpperCase(); const section = String(mesh.userData.section || '').trim().toUpperCase();
// 1. Determinação da fase e vínculo de produção // 1. Extração da fase deste elemento (Propriedade IFC > Regex na Marca)
let elementPhase = '';
if (mesh.userData.phase) {
elementPhase = String(mesh.userData.phase).trim();
}
if (!elementPhase) {
const raw = assMark || pmark;
const match = raw.match(/^(?:[A-Za-z0-9]+-)?(\d+)-/i);
if (match) {
elementPhase = match[1];
}
}
// 2. Determinação do vínculo de produção (com matching rigoroso de fase)
let prod: ProductionPieceStatus | undefined = undefined; let prod: ProductionPieceStatus | undefined = undefined;
if (colorMode === 'production' && productionData && productionData.size > 0) { if (colorMode === 'production' && productionData && productionData.size > 0) {
// Prioridade 1: Busca pela Assembly Mark do conjunto estrutural const searchKeys = [
if (assMark && assMark !== 'INDEFINIDO') { elementPhase && selectedOF ? `${selectedOF}-${elementPhase}-${cleanAssMark}` : '',
prod = productionData.get(assMark) || elementPhase ? `${elementPhase}-${cleanAssMark}` : '',
productionData.get(cleanAssMark) || elementPhase && selectedOF ? `${selectedOF}-${elementPhase}-${cleanMark}` : '',
(selectedPhase && selectedPhase !== 'all' ? productionData.get(`${selectedPhase}-${cleanAssMark}`.toUpperCase()) : undefined) || elementPhase ? `${elementPhase}-${cleanMark}` : '',
(selectedOF ? productionData.get(`${selectedOF}-${cleanAssMark}`.toUpperCase()) : undefined); assMark,
pmark,
cleanAssMark,
cleanMark
].filter(Boolean).map(k => k.toUpperCase());
for (const key of searchKeys) {
const match = productionData.get(key);
// Se encontramos, mas ele tem fase e nós temos fase, elas precisam bater.
if (match) {
if (!elementPhase || !match.fase || String(match.fase) === String(elementPhase)) {
prod = match;
break;
}
}
} }
// Prioridade 2: Busca pela marca própria da peça // Fallback para perfis / bitolas
if (!prod && pmark && pmark !== 'INDEFINIDO') {
prod = productionData.get(pmark) ||
productionData.get(cleanMark) ||
(selectedPhase && selectedPhase !== 'all' ? productionData.get(`${selectedPhase}-${cleanMark}`.toUpperCase()) : undefined) ||
(selectedOF ? productionData.get(`${selectedOF}-${cleanMark}`.toUpperCase()) : undefined);
}
// Prioridade 3: Fallback para perfis / bitolas
if (!prod && section && section !== '-' && section !== 'INDEFINIDO') { if (!prod && section && section !== '-' && section !== 'INDEFINIDO') {
prod = productionData.get(`PERFIL:${section}`) || prod = productionData.get(`PERFIL:${section}`) ||
productionData.get(`DESC:${section}`); productionData.get(`DESC:${section}`);
@@ -291,22 +311,6 @@ export const ModelViewer3D: React.FC<ModelViewer3DProps> = ({
} }
} }
// 2. Extração da fase deste elemento (Banco > Propriedade IFC > Regex na Marca)
let elementPhase = '';
if (prod && prod.fase) {
elementPhase = String(prod.fase).trim();
}
if (!elementPhase && mesh.userData.phase) {
elementPhase = String(mesh.userData.phase).trim();
}
if (!elementPhase) {
const raw = assMark || pmark;
const match = raw.match(/^(?:[A-Za-z0-9]+-)?(\d+)-/i);
if (match) {
elementPhase = match[1];
}
}
// 3. Controle Estrito de Visibilidade por Fase // 3. Controle Estrito de Visibilidade por Fase
const isVisibleByPhase = const isVisibleByPhase =
!selectedPhase || !selectedPhase ||
@@ -330,10 +334,17 @@ export const ModelViewer3D: React.FC<ModelViewer3DProps> = ({
if (prod && prod.pointedQtd > 0) { if (prod && prod.pointedQtd > 0) {
pointedCount++; pointedCount++;
// Peça apontada no banco: aplica com precisão a cor do estágio de fabricação // Cores baseadas no progresso: verde claro -> verde escuro
const stageHex = prod.processColor || '#10b981'; const qtyPercent = Math.min(prod.pointedQtd / prod.totalQtd, 1.0);
const processPercent = Math.min((prod.processOrdem || 1) / 5, 1.0);
const percent = qtyPercent * processPercent;
const colorLight = new THREE.Color('#4ade80'); // Verde claro
const colorDark = new THREE.Color('#14532d'); // Verde escuro
const finalColor = new THREE.Color().lerpColors(colorLight, colorDark, percent);
mesh.material = new THREE.MeshStandardMaterial({ mesh.material = new THREE.MeshStandardMaterial({
color: new THREE.Color(stageHex), color: finalColor,
metalness: 0.35, metalness: 0.35,
roughness: 0.45, roughness: 0.45,
side: THREE.DoubleSide, side: THREE.DoubleSide,
+41 -42
View File
@@ -562,49 +562,49 @@ export async function loadAndAuditIFC(
''; '';
// Extração inteligente de Fase // Extração inteligente de Fase
const phaseCandidates = [ let chosenPhase = extractPhaseFromMark(assemblyMark || finalMark);
props.Phase,
props.Fase, if (!chosenPhase) {
props['Phase Number'], const phaseCandidates = [
props['Phase Name'], props.Phase,
props['Tekla Assembly.Phase'], props.Fase,
props['Tekla Common.Phase'], props['Phase Number'],
props['Tekla Assembly.Phase Name'], props['Phase Name'],
props.Etapa, props['Tekla Assembly.Phase'],
props.Sequence, props['Tekla Common.Phase'],
props.Batch props['Tekla Assembly.Phase Name'],
]; props.Etapa,
let chosenPhase = ''; props.Sequence,
for (const pCand of phaseCandidates) { props.Batch
if (pCand && isValidMark(pCand)) {
chosenPhase = String(pCand).trim();
break;
}
}
if (!chosenPhase && childToAssembly.has(expressID)) {
const parentId = childToAssembly.get(expressID)!;
const parentProps = elementPropertiesMap.get(parentId) || {};
const parentPhaseCandidates = [
parentProps.Phase,
parentProps.Fase,
parentProps['Phase Number'],
parentProps['Phase Name'],
parentProps['Tekla Assembly.Phase'],
parentProps['Tekla Common.Phase'],
parentProps['Tekla Assembly.Phase Name'],
parentProps.Etapa,
parentProps.Sequence,
parentProps.Batch
]; ];
for (const pCand of parentPhaseCandidates) { for (const pCand of phaseCandidates) {
if (pCand && isValidMark(pCand)) { if (pCand && isValidMark(pCand)) {
chosenPhase = String(pCand).trim(); chosenPhase = String(pCand).trim();
break; break;
} }
} }
} if (!chosenPhase && childToAssembly.has(expressID)) {
if (!chosenPhase) { const parentId = childToAssembly.get(expressID)!;
chosenPhase = extractPhaseFromMark(assemblyMark || finalMark); const parentProps = elementPropertiesMap.get(parentId) || {};
const parentPhaseCandidates = [
parentProps.Phase,
parentProps.Fase,
parentProps['Phase Number'],
parentProps['Phase Name'],
parentProps['Tekla Assembly.Phase'],
parentProps['Tekla Common.Phase'],
parentProps['Tekla Assembly.Phase Name'],
parentProps.Etapa,
parentProps.Sequence,
parentProps.Batch
];
for (const pCand of parentPhaseCandidates) {
if (pCand && isValidMark(pCand)) {
chosenPhase = String(pCand).trim();
break;
}
}
}
} }
return { return {
@@ -631,7 +631,7 @@ export async function loadAndAuditIFC(
const markInfo = extractBestMark(assID); const markInfo = extractBestMark(assID);
const children = assemblyToChildren.get(assID) || []; const children = assemblyToChildren.get(assID) || [];
if (markInfo.phase) { if (markInfo.phase && markInfo.mark && markInfo.mark !== 'indefinido') {
detectedPhasesSet.add(markInfo.phase); detectedPhasesSet.add(markInfo.phase);
} }
@@ -679,7 +679,7 @@ export async function loadAndAuditIFC(
const materialName = elementMaterialMap.get(expressID) ?? ''; const materialName = elementMaterialMap.get(expressID) ?? '';
const connectedGroup = weldedGroupMap.get(expressID) || [expressID]; const connectedGroup = weldedGroupMap.get(expressID) || [expressID];
if (markInfo.phase) { if (markInfo.phase && markInfo.mark && markInfo.mark !== 'indefinido') {
detectedPhasesSet.add(markInfo.phase); detectedPhasesSet.add(markInfo.phase);
} }
@@ -856,14 +856,13 @@ export function extractPhaseFromMark(mark: string): string {
const parts = mark.split('-'); const parts = mark.split('-');
if (parts.length >= 3) { if (parts.length >= 3) {
const p1 = parts[1].trim(); const p1 = parts[1].trim();
if (p1 && /^\d+$/.test(p1)) return p1; if (p1 && /^[a-zA-Z0-9]+$/.test(p1)) return p1; // Can be 11, 12, A, etc.
return p1;
} }
if (parts.length === 2) { if (parts.length === 2) {
const p0 = parts[0].trim(); const p0 = parts[0].trim();
if (p0 && /^\d+$/.test(p0)) return p0; if (p0 && /^\d+$/.test(p0)) return p0;
} }
const match = mark.match(/(?:fase|phase|etapa)\s*(\d+)/i); const match = mark.match(/(?:fase|phase|etapa)\s*([a-zA-Z0-9]+)/i);
if (match) return match[1]; if (match) return match[1];
return ''; return '';
+2 -2
View File
@@ -133,8 +133,8 @@ export default function Visualizador3D() {
if (pecasData) { if (pecasData) {
pecasData.forEach((p: any) => { pecasData.forEach((p: any) => {
const fase = String(p.etapa_fase || '1'); const fase = String(p.etapa_fase || '').trim();
phases.add(fase); if (fase) phases.add(fase);
totalPecas += Number(p.quantidade || 0); totalPecas += Number(p.quantidade || 0);
totalPeso += Number(p.peso_total || 0); totalPeso += Number(p.peso_total || 0);