s3 / index.html
Diegobrons's picture
Add 2 files
5b7fcf5 verified
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Identificador de Plásticos - ReciclaTech</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4/dist/tesseract.min.js"></script>
<style>
.sidebar {
width: 280px;
transition: all 0.3s;
}
.sidebar-collapsed {
width: 80px;
}
.main-content {
margin-left: 280px;
transition: all 0.3s;
}
.main-content-expanded {
margin-left: 80px;
}
.drop-zone {
border: 2px dashed #ccc;
border-radius: 8px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.drop-zone.highlight {
border-color: #3b82f6;
background-color: rgba(59, 130, 246, 0.1);
}
.plastic-badge {
font-size: 12px;
padding: 2px 8px;
border-radius: 10px;
font-weight: bold;
}
.plastic-PP { background-color: #0077B6; color: white; }
.plastic-ABS { background-color: #E63946; color: white; }
.plastic-PC { background-color: #1D3557; color: white; }
.plastic-PVC { background-color: #457B9D; color: white; }
.plastic-PET { background-color: #2A9D8F; color: white; }
.plastic-HIPS { background-color: #F4A261; color: white; }
.plastic-PS { background-color: #E9C46A; color: white; }
.plastic-OTHER { background-color: #6A4C93; color: white; }
.progress-bar {
height: 8px;
background-color: #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background-color: #3b82f6;
transition: width 0.3s ease;
}
.composite-warning {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { border-left-color: #f59e0b; }
50% { border-left-color: #fbbf24; }
100% { border-left-color: #f59e0b; }
}
</style>
</head>
<body class="bg-gray-100 font-sans flex">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-gray-800 text-white h-screen fixed">
<div class="p-4 flex items-center justify-between border-b border-gray-700">
<div class="flex items-center space-x-2">
<i class="fas fa-recycle text-2xl text-blue-400"></i>
<span id="logo-text" class="font-bold text-lg">ReciclaTech</span>
</div>
<button id="toggle-sidebar" class="text-gray-400 hover:text-white">
<i class="fas fa-bars"></i>
</button>
</div>
<nav class="p-4">
<div class="mb-8">
<div class="text-gray-400 text-xs uppercase font-bold mb-2" id="nav-section-title">Menu Principal</div>
<ul class="space-y-2">
<li>
<a href="#" class="flex items-center space-x-3 p-2 rounded bg-gray-700 text-white">
<i class="fas fa-home w-6 text-center"></i>
<span id="nav-dashboard" class="font-medium">Dashboard</span>
</a>
</li>
<li>
<a href="#" class="flex items-center space-x-3 p-2 rounded hover:bg-gray-700 text-gray-300 hover:text-white">
<i class="fas fa-tasks w-6 text-center"></i>
<span id="nav-processos" class="font-medium">Processos</span>
</a>
</li>
<li>
<a href="#" class="flex items-center space-x-3 p-2 rounded hover:bg-gray-700 text-gray-300 hover:text-white">
<i class="fas fa-warehouse w-6 text-center"></i>
<span id="nav-estoque" class="font-medium">Estoque</span>
</a>
</li>
</ul>
</div>
<div>
<div class="text-gray-400 text-xs uppercase font-bold mb-2" id="tools-section-title">Ferramentas</div>
<ul class="space-y-2">
<li>
<a href="#" class="flex items-center space-x-3 p-2 rounded bg-blue-900 text-white">
<i class="fas fa-camera w-6 text-center"></i>
<span id="nav-identificador" class="font-medium">Identificador</span>
</a>
</li>
<li>
<a href="#" class="flex items-center space-x-3 p-2 rounded hover:bg-gray-700 text-gray-300 hover:text-white">
<i class="fas fa-barcode w-6 text-center"></i>
<span id="nav-etiquetas" class="font-medium">Etiquetas</span>
</a>
</li>
</ul>
</div>
</nav>
<div class="absolute bottom-0 left-0 right-0 p-4 border-t border-gray-700">
<button id="updatePricesBtn" class="flex items-center space-x-3 p-2 rounded hover:bg-gray-700 text-gray-300 hover:text-white w-full text-left">
<i class="fas fa-sync-alt w-6 text-center"></i>
<span id="nav-update" class="font-medium">Atualizar Preços</span>
</button>
</div>
</div>
<!-- Main Content -->
<div id="main-content" class="main-content flex-1 p-6">
<div class="bg-white rounded-lg shadow-md p-6">
<!-- Header -->
<div class="flex justify-between items-center mb-6">
<div>
<h1 class="text-2xl font-bold text-gray-800">Identificador de Plásticos</h1>
<p class="text-gray-600">Classifique plásticos de lixo eletrônico por símbolos e códigos</p>
</div>
<div class="flex space-x-3">
<button id="helpBtn" class="bg-blue-50 hover:bg-blue-100 text-blue-700 px-4 py-2 rounded-lg font-medium flex items-center">
<i class="fas fa-question-circle mr-2"></i> Ajuda
</button>
</div>
</div>
<!-- Tool Section -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Upload Section -->
<div class="lg:col-span-2">
<div class="bg-gray-50 rounded-lg p-6">
<h2 class="text-lg font-semibold mb-4">Enviar imagem para análise</h2>
<div id="dropZone" class="drop-zone mb-6">
<i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-3"></i>
<h3 class="text-lg font-medium mb-1">Arraste e solte a imagem aqui</h3>
<p class="text-gray-500 mb-4">Formatos: JPG, PNG, WEBP</p>
<button id="selectFileBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg font-medium transition">
Selecionar arquivo
</button>
<input type="file" id="fileInput" accept="image/*" class="hidden">
</div>
<div id="previewContainer" class="hidden">
<div class="flex justify-between items-center mb-3">
<h3 class="text-lg font-medium">Pré-visualização</h3>
<button id="cancelBtn" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i> Cancelar
</button>
</div>
<div class="border border-gray-200 rounded-lg p-4">
<img id="imagePreview" src="#" alt="Pré-visualização" class="max-w-full h-auto rounded mx-auto">
</div>
<div class="mt-4 grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Peso (kg)</label>
<input type="number" id="weightInput" step="0.01" min="0" placeholder="Opcional" class="w-full p-2 border rounded">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Origem</label>
<select id="sourceInput" class="w-full p-2 border rounded">
<option value="">Selecione...</option>
<option value="carcaca">Carcaça</option>
<option value="componente">Componente Interno</option>
<option value="fio">Fios/Cabos</option>
<option value="outro">Outro</option>
</select>
</div>
</div>
<button id="analyzeBtn" class="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white py-3 rounded-lg font-semibold transition">
<i class="fas fa-search mr-2"></i> Analisar Material
</button>
</div>
<div id="loadingIndicator" class="hidden text-center py-8">
<div class="inline-block animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-blue-500 mb-4"></div>
<p class="text-gray-600">Analisando o material plástico...</p>
<div class="progress-bar mt-2">
<div id="analysisProgress" class="progress-bar-fill" style="width: 0%"></div>
</div>
<p id="progressText" class="text-sm text-gray-500 mt-2">Iniciando análise...</p>
</div>
</div>
</div>
<!-- Results Section -->
<div>
<div class="bg-gray-50 rounded-lg p-6 h-full">
<h2 class="text-lg font-semibold mb-4">Resultados da Análise</h2>
<div id="resultsContainer" class="hidden">
<div id="resultsContent" class="space-y-4">
<!-- Results will be inserted here by JavaScript -->
</div>
<div id="confirmationSection" class="mt-6 hidden">
<h3 class="font-semibold mb-2">Confirmar identificação</h3>
<div class="flex space-x-3">
<button id="confirmCorrectBtn" class="flex-1 bg-green-600 hover:bg-green-700 text-white py-2 rounded-lg font-medium">
<i class="fas fa-check mr-2"></i> Correto
</button>
<button id="confirmWrongBtn" class="flex-1 bg-red-600 hover:bg-red-700 text-white py-2 rounded-lg font-medium">
<i class="fas fa-times mr-2"></i> Incorreto
</button>
</div>
</div>
<div id="storageSection" class="mt-6 hidden">
<h3 class="font-semibold mb-2">Armazenar resultado</h3>
<div class="flex space-x-3">
<button id="storeResultBtn" class="flex-1 bg-blue-600 hover:bg-blue-700 text-white py-2 rounded-lg font-medium">
<i class="fas fa-save mr-2"></i> Salvar
</button>
<button id="discardResultBtn" class="flex-1 bg-gray-600 hover:bg-gray-700 text-white py-2 rounded-lg font-medium">
<i class="fas fa-trash mr-2"></i> Descartar
</button>
</div>
</div>
</div>
<div id="emptyState" class="text-center py-8">
<i class="fas fa-microscope text-4xl text-gray-300 mb-3"></i>
<h3 class="text-gray-500 font-medium">Nenhuma análise realizada</h3>
<p class="text-sm text-gray-400 mt-1">Envie uma imagem para identificar o plástico</p>
</div>
</div>
</div>
</div>
<!-- Recent Analyses -->
<div class="mt-8">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold">Análises Recentes</h2>
<button id="clearHistoryBtn" class="text-sm text-gray-500 hover:text-gray-700">
<i class="fas fa-trash-alt mr-1"></i> Limpar histórico
</button>
</div>
<div class="bg-gray-50 rounded-lg p-4 overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-100">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Data</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Material</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Origem</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Valor</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações</th>
</tr>
</thead>
<tbody id="recentAnalysesTable" class="bg-white divide-y divide-gray-200">
<!-- Recent analyses will be inserted here by JavaScript -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Help Modal -->
<div id="helpModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg p-6 max-w-2xl max-h-[90vh] overflow-y-auto">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold">Ajuda - Identificador de Plásticos</h3>
<button id="closeHelpModal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="space-y-4">
<div>
<h4 class="font-semibold text-lg mb-2">Como usar:</h4>
<ol class="list-decimal pl-5 space-y-2">
<li>Tire uma foto nítida do símbolo de reciclagem ou código do plástico</li>
<li>Envie a imagem arrastando ou clicando no botão</li>
<li>Adicione o peso (opcional) para cálculo de valor</li>
<li>Confirme o resultado e armazene para histórico</li>
</ol>
</div>
<div>
<h4 class="font-semibold text-lg mb-2">Códigos reconhecidos:</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-50 p-3 rounded-lg">
<span class="plastic-badge plastic-PET">PET</span>
<p class="mt-1 text-sm">1 - Politereftalato de Etileno (Garrafas, fibras)</p>
</div>
<div class="bg-gray-50 p-3 rounded-lg">
<span class="plastic-badge plastic-PP">PP</span>
<p class="mt-1 text-sm">5 - Polipropileno (Embalagens, autopeças)</p>
</div>
<div class="bg-gray-50 p-3 rounded-lg">
<span class="plastic-badge plastic-ABS">ABS</span>
<p class="mt-1 text-sm">Acrilonitrila Butadieno Estireno (Eletrônicos)</p>
</div>
<div class="bg-gray-50 p-3 rounded-lg">
<span class="plastic-badge plastic-PS">PS</span>
<p class="mt-1 text-sm">6 - Poliestireno (Embalagens, isolamento)</p>
</div>
</div>
</div>
<div class="bg-blue-50 border-l-4 border-blue-400 p-4">
<h4 class="font-semibold text-lg mb-2">Dicas para melhor reconhecimento:</h4>
<ul class="list-disc pl-5 space-y-1">
<li>Fotografe em boa luz e com o símbolo em foco</li>
<li>Para peças mistas, fotografe cada código separadamente</li>
<li>Limpe a peça antes de fotografar para melhor visibilidade</li>
</ul>
</div>
</div>
</div>
</div>
<script>
// Enhanced Plastic Database with recycling codes
const plasticos_db = {
"PET": {
"nome": "Politereftalato de Etileno",
"codigo": 1,
"reciclavel": true,
"valor_kg": 1.80,
"grupo": "Verde",
"densidade": "1.38-1.39 g/cm³",
"descricao": "Comum em garrafas de bebidas and fibras têxteis. Reciclável, mas não reutilizável para alimentos.",
"aplicacao": "Garrafas, fibras, embalagens alimentícias",
"updateUrl": "https://api.reciclagem.com/precos/PET"
},
"PP": {
"nome": "Polipropileno",
"codigo": 5,
"reciclavel": true,
"valor_kg": 1.60,
"grupo": "Azul",
"densidade": "0.89-0.91 g/cm³",
"descricao": "Usado em embalagens alimentícias, seringas and autopeças. Resistente a altas temperaturas.",
"aplicacao": "Embalagens, autopeças, utensílios médicos",
"updateUrl": "https://api.reciclagem.com/precos/PP"
},
"ABS": {
"nome": "Acrilonitrila Butadieno Estireno",
"codigo": null,
"reciclavel": true,
"valor_kg": 2.40,
"grupo": "Vermelho",
"densidade": "1.04-1.06 g/cm³",
"descricao": "Presente em teclados, carcaças de eletrônicos and brinquedos. Resistente ao impacto.",
"aplicacao": "Eletrônicos, brinquedos, peças automotivas",
"updateUrl": "https://api.reciclagem.com/precos/ABS"
},
"PS": {
"nome": "Poliestireno",
"codigo": 6,
"reciclavel": true,
"valor_kg": 1.20,
"grupo": "Amarelo",
"densidade": "1.04-1.07 g/cm³",
"descricao": "Usado em embalagens, copos descartáveis and isolamento térmico.",
"aplicacao": "Embalagens, isolamento, utensílios descartáveis",
"updateUrl": "https://api.reciclagem.com/precos/PS"
},
"OTHER": {
"nome": "Outros Plásticos",
"codigo": 7,
"reciclavel": false,
"valor_kg": 0.80,
"grupo": "Preto",
"densidade": "Varia",
"descricao": "Mistura de plásticos ou tipos não classificados. Pode conter BPA.",
"aplicacao": "Variada",
"updateUrl": "https://api.reciclagem.com/precos/OTHER"
}
};
// Map numeric codes to plastic types
const codeToPlastic = {
1: "PET",
2: "HDPE",
5: "PP",
6: "PS",
7: "OTHER"
};
// Variables
let imageToAnalyze;
let currentAnalysis = null;
let recentAnalyses = JSON.parse(localStorage.getItem('recentAnalyses')) || [];
// DOM elements
const sidebar = document.getElementById('sidebar');
const mainContent = document.getElementById('main-content');
const toggleSidebarBtn = document.getElementById('toggle-sidebar');
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const selectFileBtn = document.getElementById('selectFileBtn');
const previewContainer = document.getElementById('previewContainer');
const imagePreview = document.getElementById('imagePreview');
const analyzeBtn = document.getElementById('analyzeBtn');
const cancelBtn = document.getElementById('cancelBtn');
const loadingIndicator = document.getElementById('loadingIndicator');
const resultsContainer = document.getElementById('resultsContainer');
const resultsContent = document.getElementById('resultsContent');
const emptyState = document.getElementById('emptyState');
const confirmationSection = document.getElementById('confirmationSection');
const storageSection = document.getElementById('storageSection');
const confirmCorrectBtn = document.getElementById('confirmCorrectBtn');
const confirmWrongBtn = document.getElementById('confirmWrongBtn');
const storeResultBtn = document.getElementById('storeResultBtn');
const discardResultBtn = document.getElementById('discardResultBtn');
const weightInput = document.getElementById('weightInput');
const sourceInput = document.getElementById('sourceInput');
const analysisProgress = document.getElementById('analysisProgress');
const progressText = document.getElementById('progressText');
const recentAnalysesTable = document.getElementById('recentAnalysesTable');
const clearHistoryBtn = document.getElementById('clearHistoryBtn');
const updatePricesBtn = document.getElementById('updatePricesBtn');
const helpBtn = document.getElementById('helpBtn');
const helpModal = document.getElementById('helpModal');
const closeHelpModal = document.getElementById('closeHelpModal');
// Initialize the app
document.addEventListener('DOMContentLoaded', () => {
updateRecentAnalysesTable();
setupEventListeners();
});
function setupEventListeners() {
// Toggle sidebar
toggleSidebarBtn.addEventListener('click', toggleSidebar);
// File selection events - CORREÇÃO AQUI
selectFileBtn.addEventListener('click', function() {
fileInput.click();
});
fileInput.addEventListener('change', function(e) {
if (this.files && this.files[0]) {
handleFile(this.files[0]);
}
});
analyzeBtn.addEventListener('click', analyzeImage);
cancelBtn.addEventListener('click', resetTool);
// Confirmation and storage events
confirmCorrectBtn.addEventListener('click', () => confirmAnalysis(true));
confirmWrongBtn.addEventListener('click', () => confirmAnalysis(false));
storeResultBtn.addEventListener('click', storeAnalysis);
discardResultBtn.addEventListener('click', resetTool);
// Drag and drop events
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, preventDefaults, false);
});
['dragenter', 'dragover'].forEach(eventName => {
dropZone.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, unhighlight, false);
});
dropZone.addEventListener('drop', handleDrop, false);
// Other buttons
clearHistoryBtn.addEventListener('click', clearHistory);
updatePricesBtn.addEventListener('click', updatePrices);
helpBtn.addEventListener('click', () => helpModal.classList.remove('hidden'));
closeHelpModal.addEventListener('click', () => helpModal.classList.add('hidden'));
}
function toggleSidebar() {
sidebar.classList.toggle('sidebar-collapsed');
mainContent.classList.toggle('main-content-expanded');
// Toggle text elements in sidebar
document.querySelectorAll('[id$="-text"], [id^="nav-"], [id$="-title"]').forEach(el => {
el.classList.toggle('hidden');
});
}
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
function highlight() {
dropZone.classList.add('highlight');
}
function unhighlight() {
dropZone.classList.remove('highlight');
}
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0 && files[0].type.match('image.*')) {
handleFile(files[0]);
}
}
function handleFile(file) {
const reader = new FileReader();
reader.onload = function(event) {
imagePreview.src = event.target.result;
imageToAnalyze = imagePreview;
previewContainer.classList.remove('hidden');
dropZone.classList.add('hidden');
emptyState.classList.add('hidden');
resultsContainer.classList.add('hidden');
};
reader.readAsDataURL(file);
}
// Update progress bar and text
function updateProgress(percent, text) {
analysisProgress.style.width = `${percent}%`;
progressText.textContent = text;
}
// Analyze the image
async function analyzeImage() {
if (!imageToAnalyze) return;
try {
// Show loading indicator
previewContainer.classList.add('hidden');
loadingIndicator.classList.remove('hidden');
resultsContainer.classList.add('hidden');
// Get weight and source if provided
const weight = weightInput.value ? parseFloat(weightInput.value) : null;
const source = sourceInput.value || null;
// Step 1: Perform OCR to find plastic codes
updateProgress(30, 'Procurando códigos de plástico na imagem...');
const ocrResult = await performOCR(imagePreview);
// Step 2: Identify plastic type based on found codes
updateProgress(70, 'Identificando tipo de plástico...');
const plasticTypes = identifyPlasticTypes(ocrResult.foundCodes, ocrResult.text);
// Get current time for the analysis
const now = new Date();
const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
const dateString = now.toLocaleDateString();
// Store current analysis
currentAnalysis = {
plasticTypes: plasticTypes,
weight: weight,
source: source,
image: imagePreview.src,
confirmed: false,
stored: false,
date: dateString,
time: timeString,
identifiedBy: 'OCR',
confidence: plasticTypes.length > 0 ? 0.95 : 0.70
};
// Display results
displayResults(currentAnalysis);
updateProgress(100, 'Análise concluída!');
// Small delay to show 100% progress
await new Promise(resolve => setTimeout(resolve, 500));
} catch (error) {
console.error('Error analyzing image:', error);
showError('Erro ao analisar a imagem. Por favor, tente novamente com uma foto mais nítida.');
resetTool();
}
}
// Perform OCR on the image
async function performOCR(imageElement) {
try {
const { data: { text } } = await Tesseract.recognize(
imageElement,
'por+eng', // Portuguese and English
{
logger: m => {
if (m.status === 'recognizing text') {
updateProgress(30 + (m.progress * 40), 'Lendo códigos na imagem...');
}
}
}
);
// Check for common plastic codes (PP, ABS, PC, etc.)
const plasticCodes = text.match(/\b(PP|ABS|PC|PVC|PET|HIPS|PMMA|PA|POM|PBT|PSU|PEI|PEEK|PTFE)\b/gi) || [];
// Also check for numeric codes (1-7)
const numericCodes = text.match(/\b[1-7]\b/g) || [];
// Combine all found codes (uppercase)
const allCodes = [
...plasticCodes.map(code => code.toUpperCase()),
...numericCodes.map(code => codeToPlastic[code] || null).filter(Boolean)
];
return {
text: text,
foundCodes: [...new Set(allCodes)] // Remove duplicates
};
} catch (error) {
console.error('OCR error:', error);
return {
text: '',
foundCodes: []
};
}
}
// Identify plastic types based on found codes
function identifyPlasticTypes(foundCodes, ocrText) {
// If no codes found, try to identify from text description
if (foundCodes.length === 0) {
const textIdentification = identifyFromText(ocrText);
if (textIdentification) {
return [textIdentification];
}
return [{
type: 'OTHER',
...plasticos_db['OTHER'],
badgeClass: 'plastic-OTHER',
confidence: 0.70
}];
}
// Return all identified plastics
return foundCodes.map(code => {
const plasticInfo = plasticos_db[code] || plasticos_db['OTHER'];
return {
type: code,
...plasticInfo,
badgeClass: `plastic-${code}`,
confidence: 0.95
};
});
}
// Try to identify plastic from text description
function identifyFromText(text) {
const lowerText = text.toLowerCase();
// Check for common plastic descriptions
if (lowerText.includes('polipropileno') || lowerText.includes('polyprop')) {
return {
type: 'PP',
...plasticos_db['PP'],
badgeClass: 'plastic-PP',
confidence: 0.85
};
}
if (lowerText.includes('poliestireno') || lowerText.includes('polystyrene')) {
return {
type: 'PS',
...plasticos_db['PS'],
badgeClass: 'plastic-PS',
confidence: 0.85
};
}
return null;
}
// Display the analysis results
function displayResults(analysis) {
loadingIndicator.classList.add('hidden');
emptyState.classList.add('hidden');
// Calculate total value if weight is provided
const totalValue = analysis.weight && analysis.plasticTypes.length > 0 ?
(analysis.weight * analysis.plasticTypes[0].valor_kg).toFixed(2) :
null;
// Create results HTML
let html = '';
// Show warning for composite materials
if (analysis.plasticTypes.length > 1) {
html += `
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-4 composite-warning">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-exclamation-triangle text-yellow-500"></i>
</div>
<div class="ml-3">
<p class="text-sm text-yellow-700">
<strong>Material composto encontrado!</strong><br>
Esta peça contém múltiplos plásticos:
${analysis.plasticTypes.map(t => t.type).join(' + ')}.
Separe manualmente se possível.
</p>
</div>
</div>
</div>
`;
}
// Show info for each plastic type found
analysis.plasticTypes.forEach(plasticType => {
const plasticValue = analysis.weight ?
(analysis.weight * plasticType.valor_kg).toFixed(2) :
null;
html += `
<div class="bg-white border border-gray-200 rounded-lg p-4 mb-4">
<div class="flex justify-between items-start mb-3">
<div>
<span class="${plasticType.badgeClass} plastic-badge">${plasticType.type}</span>
${plasticType.codigo ? `<span class="ml-2 text-sm text-gray-500">Código: ${plasticType.codigo}</span>` : ''}
</div>
<div class="text-sm text-gray-500">${analysis.date} ${analysis.time}</div>
</div>
<h3 class="font-bold text-lg mb-1">${plasticType.nome}</h3>
<p class="text-sm text-gray-600 mb-3">${plasticType.descricao}</p>
<div class="grid grid-cols-2 gap-4 mb-3">
<div>
<div class="text-xs text-gray-500">Reciclável</div>
<div class="font-medium">${plasticType.reciclavel ? 'Sim' : 'Não'}</div>
</div>
<div>
<div class="text-xs text-gray-500">Valor por kg</div>
<div class="font-medium">R$ ${plasticType.valor_kg.toFixed(2)}</div>
</div>
<div>
<div class="text-xs text-gray-500">Densidade</div>
<div class="font-medium">${plasticType.densidade}</div>
</div>
${analysis.weight ? `
<div>
<div class="text-xs text-gray-500">Valor estimado</div>
<div class="font-medium">R$ ${plasticValue}</div>
</div>
` : ''}
</div>
<div class="flex items-center mb-2">
<div class="w-full mr-2">
<div class="progress-bar">
<div class="progress-bar-fill" style="width: ${Math.round(plasticType.confidence * 100)}%"></div>
</div>
</div>
<span class="text-sm font-medium">${Math.round(plasticType.confidence * 100)}%</span>
</div>
<div class="text-xs text-gray-500 mb-3">Aplicação comum: ${plasticType.aplicacao}</div>
</div>
`;
});
// Add storage recommendations
if (analysis.plasticTypes.length > 0) {
const mainPlastic = analysis.plasticTypes[0];
html += `
<div class="bg-white border border-gray-200 rounded-lg p-4">
<h4 class="font-semibold mb-2">Próximos passos recomendados</h4>
<ul class="space-y-2 text-sm">
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span>Separar na área de plásticos <strong>${mainPlastic.grupo}</strong> (${mainPlastic.type})</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span>${mainPlastic.reciclavel ? 'Encaminhar para reciclagem' : 'Descarte especial necessário'}</span>
</li>
${analysis.weight ? `
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span>Valor estimado: R$ ${totalValue}</span>
</li>
` : ''}
${analysis.source ? `
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span>Origem: ${getSourceName(analysis.source)}</span>
</li>
` : ''}
</ul>
</div>
`;
}
resultsContent.innerHTML = html;
resultsContainer.classList.remove('hidden');
confirmationSection.classList.remove('hidden');
storageSection.classList.add('hidden');
}
function getSourceName(sourceKey) {
const sources = {
'carcaca': 'Carcaça',
'componente': 'Componente Interno',
'fio': 'Fios/Cabos',
'outro': 'Outro'
};
return sources[sourceKey] || sourceKey;
}
// Confirm analysis (correct or incorrect)
function confirmAnalysis(isCorrect) {
if (!currentAnalysis) return;
currentAnalysis.confirmed = isCorrect;
if (isCorrect) {
// Show storage options
confirmationSection.classList.add('hidden');
storageSection.classList.remove('hidden');
// Update results to show confirmation
const confirmationBadge = document.createElement('div');
confirmationBadge.className = 'bg-green-100 text-green-800 text-xs px-2 py-1 rounded mb-3 flex items-center';
confirmationBadge.innerHTML = `<i class="fas fa-check-circle mr-1"></i> Identificação confirmada como correta`;
resultsContent.prepend(confirmationBadge);
} else {
// Allow user to try again
showError('Identificação marcada como incorreta. Por favor, revise ou tente novamente.');
confirmationSection.classList.add('hidden');
// Add button to try again
const tryAgainBtn = document.createElement('button');
tryAgainBtn.className = 'mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded-lg font-medium';
tryAgainBtn.innerHTML = '<i class="fas fa-redo mr-2"></i> Tentar novamente';
tryAgainBtn.addEventListener('click', resetTool);
resultsContent.appendChild(tryAgainBtn);
}
}
// Store the analysis result
function storeAnalysis() {
if (!currentAnalysis) return;
currentAnalysis.stored = true;
currentAnalysis.storedAt = new Date().toISOString();
// Add to recent analyses
recentAnalyses.unshift({
...currentAnalysis,
// Store only the first plastic type for the table view
mainPlastic: currentAnalysis.plasticTypes[0]
});
// Keep only last 50 analyses
if (recentAnalyses.length > 50) {
recentAnalyses = recentAnalyses.slice(0, 50);
}
// Save to localStorage
localStorage.setItem('recentAnalyses', JSON.stringify(recentAnalyses));
// Update recent analyses table
updateRecentAnalysesTable();
// Show success message
const storageBadge = document.createElement('div');
storageBadge.className = 'bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded mb-3 flex items-center';
storageBadge.innerHTML = `<i class="fas fa-check-circle mr-1"></i> Resultado armazenado com sucesso`;
resultsContent.prepend(storageBadge);
// Hide storage section
storageSection.classList.add('hidden');
// Add button to analyze another image
const newAnalysisBtn = document.createElement('button');
newAnalysisBtn.className = 'mt-4 w-full bg-green-600 hover:bg-green-700 text-white py-2 rounded-lg font-medium';
newAnalysisBtn.innerHTML = '<i class="fas fa-camera mr-2"></i> Analisar outro material';
newAnalysisBtn.addEventListener('click', resetTool);
resultsContent.appendChild(newAnalysisBtn);
}
// Update recent analyses table
function updateRecentAnalysesTable() {
let html = '';
recentAnalyses.slice(0, 10).forEach(analysis => {
const plastic = analysis.mainPlastic || analysis.plasticTypes[0] || plasticos_db['OTHER'];
const totalValue = analysis.weight ?
(analysis.weight * plastic.valor_kg).toFixed(2) :
'-';
html += `
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${analysis.date}</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="${plastic.badgeClass} plastic-badge">${plastic.type}</span>
<span class="ml-2 text-xs text-gray-500">${plastic.nome}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
${analysis.source ? getSourceName(analysis.source) : '-'}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
${totalValue === '-' ? '-' : `R$ ${totalValue}`}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<button class="text-blue-600 hover:text-blue-800 mr-3" onclick="viewAnalysis('${analysis.storedAt}')">
<i class="fas fa-eye"></i>
</button>
<button class="text-green-600 hover:text-green-800" onclick="printLabel('${analysis.storedAt}')">
<i class="fas fa-tag"></i>
</button>
</td>
</tr>
`;
});
if (recentAnalyses.length === 0) {
html = `
<tr>
<td colspan="5" class="px-6 py-4 text-center text-sm text-gray-500">
Nenhuma análise recente encontrada
</td>
</tr>
`;
}
recentAnalysesTable.innerHTML = html;
}
// View a stored analysis
window.viewAnalysis = function(storedAt) {
const analysis = recentAnalyses.find(a => a.storedAt === storedAt);
if (analysis) {
currentAnalysis = analysis;
imagePreview.src = analysis.image;
previewContainer.classList.remove('hidden');
dropZone.classList.add('hidden');
displayResults(analysis);
// Scroll to results
resultsContainer.scrollIntoView({ behavior: 'smooth' });
}
};
// Print label for a stored analysis
window.printLabel = function(storedAt) {
const analysis = recentAnalyses.find(a => a.storedAt === storedAt);
if (analysis) {
const plastic = analysis.mainPlastic || analysis.plasticTypes[0] || plasticos_db['OTHER'];
const win = window.open('', '_blank');
win.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>Etiqueta - ${plastic.type}</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 10mm; }
.label { width: 80mm; height: 50mm; border: 1px solid #ccc; padding: 5mm; }
.badge { display: inline-block; padding: 2px 8px; border-radius: 10px; color: white; font-weight: bold; }
h2 { margin: 0 0 5px 0; }
p { margin: 2px 0; font-size: 14px; }
</style>
</head>
<body>
<div class="label">
<h2>ReciclaTech</h2>
<p><span class="badge" style="background: ${getComputedStyle(document.querySelector(`.${plastic.badgeClass}`)).backgroundColor}">${plastic.type}</span></p>
<p><strong>${plastic.nome}</strong></p>
<p>${analysis.date} ${analysis.time}</p>
${analysis.weight ? `<p>Peso: ${analysis.weight} kg</p>` : ''}
${analysis.source ? `<p>Origem: ${getSourceName(analysis.source)}</p>` : ''}
<p>Valor/kg: R$ ${plastic.valor_kg.toFixed(2)}</p>
</div>
<script>
setTimeout(() => { window.print(); window.close(); }, 200);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Diegobrons/s3" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>