Teste de filtros de projetos
Deploy / Deploy (push) Successful in 1m21s Details

This commit is contained in:
JotaChina 2025-12-13 21:59:59 -03:00
parent fc6f447be4
commit dd837d6d6b
1 changed files with 112 additions and 62 deletions

View File

@ -37,6 +37,26 @@ permalink: /projetos/
<div class="column is-narrow">
<div class="field has-addons">
<div class="control">
<span class="button is-static is-small is-rounded">
<i class="fa-solid fa-star"></i>
</span>
</div>
<div class="control">
<div class="select is-small">
<select id="star-filter">
<option value="all">Todas Complexidades</option>
<option value="5">⭐⭐⭐⭐⭐ (5) Expert</option>
<option value="4">⭐⭐⭐⭐ (4+) Avançado</option>
<option value="3">⭐⭐⭐ (3+) Intermediário</option>
<option value="2">⭐⭐ (2+) Básico</option>
</select>
</div>
</div>
<div class="control"><span class="mx-1"></span></div>
<div class="control">
<a class="button is-static is-small is-rounded">
<i class="fa-solid fa-sort"></i>
@ -46,6 +66,8 @@ permalink: /projetos/
<div class="select is-small is-rounded">
<select id="sort-select">
<option value="default">Ordem Padrão</option>
<option value="stars_desc">⭐ Mais Complexos</option>
<option value="stars_asc">⭐ Mais Simples</option>
<option value="intensity_desc">🔥 Mais Intensos (dB)</option>
<option value="intensity_asc">❄️ Mais Calmos (dB)</option>
<option value="bpm_desc">⚡ BPM (Rápido)</option>
@ -53,6 +75,7 @@ permalink: /projetos/
</select>
</div>
</div>
</div>
</div>
</div>
@ -260,7 +283,7 @@ permalink: /projetos/
<script>
document.addEventListener('DOMContentLoaded', () => {
// ===============================================
// 1. CONFIGURAÇÃO E MODAL (Existente)
// 1. CONFIGURAÇÃO E MODAL
// ===============================================
const modal = document.getElementById('preview-modal');
const iframe = document.getElementById('preview-iframe');
@ -304,17 +327,21 @@ document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('keydown', (e) => { if (e.key === "Escape") closeModal(); });
// ===============================================
// 2. LÓGICA DE INTEGRAÇÃO COM IA (Novo)
// 2. LÓGICA DE DADOS (IA / FILTROS / ORDENAÇÃO)
// ===============================================
// URL para o JSON que você gerou com o Python. Ajuste se necessário.
const JSON_URL = '/mmpSearch/src_mmpSearch/saida_analises/db_final_completo.json';
const cards = document.querySelectorAll('.project-card');
const genreContainer = document.getElementById('genre-filters');
const sortSelect = document.getElementById('sort-select');
const starFilterSelect = document.getElementById('star-filter'); // NOVO SELETOR
const projectsContainer = document.getElementById('projects-container');
// Estado global dos filtros
let currentGenre = 'all';
let currentMinStars = 'all';
// Inicializa a carga de dados
fetch(JSON_URL)
.then(response => {
@ -328,19 +355,19 @@ document.addEventListener('DOMContentLoaded', () => {
})
.catch(err => {
console.warn("Aviso: Dados da IA não puderam ser carregados.", err);
// Oculta os filtros se não houver dados
const filterBox = document.querySelector('.box.has-background-white-ter');
if(filterBox) filterBox.style.display = 'none';
});
// Função auxiliar para gerar estrelas HTML
function gerarEstrelas(num) {
let html = '<span class="has-text-warning" title="Nível de Complexidade Técnica">';
for (let i = 0; i < 5; i++) {
if (i < num) {
html += '<i class="fa-solid fa-star"></i>'; // Estrela cheia
// Garante que num seja inteiro
const n = Math.round(num);
let html = '<span class="has-text-warning" title="Nível de Complexidade: '+n+'/5">';
for (let i = 1; i <= 5; i++) {
if (i <= n) {
html += '<i class="fa-solid fa-star"></i>';
} else {
html += '<i class="fa-regular fa-star"></i>'; // Estrela vazia
html += '<i class="fa-regular fa-star" style="opacity:0.4"></i>';
}
}
html += '</span>';
@ -348,7 +375,6 @@ document.addEventListener('DOMContentLoaded', () => {
}
function enrichCards(data) {
// 1. Cria um mapa para busca rápida
const mapDados = {};
data.forEach(item => {
const keyArquivo = normalizarChaveJS(item.arquivo.replace('.wav','').replace('.mp3',''));
@ -362,56 +388,55 @@ document.addEventListener('DOMContentLoaded', () => {
cards.forEach(card => {
const cardKey = normalizarChaveJS(card.dataset.title);
// 2. Busca instantânea
const info = mapDados[cardKey];
if (info) {
// --- 1. Metadados para Filtros ---
card.dataset.genre = (info.analise_ia && info.analise_ia.genero_macro) ? info.analise_ia.genero_macro : "Unknown";
card.dataset.intensity = (info.analise_tecnica && info.analise_tecnica.intensidade_db) ? parseFloat(info.analise_tecnica.intensidade_db) : 0;
card.dataset.bpm_real = (info.analise_tecnica && info.analise_tecnica.bpm) ? parseFloat(info.analise_tecnica.bpm) : 0;
// --- 2. Lógica das Estrelas ---
let numeroEstrelas = 0;
// Verifica se existe o objeto complexidade e o valor estrelas
if (info.analise_tecnica && info.analise_tecnica.complexidade && info.analise_tecnica.complexidade.estrelas) {
numeroEstrelas = info.analise_tecnica.complexidade.estrelas;
// --- 1. Extração de Dados ---
const genero = (info.analise_ia?.genero_macro) || "Unknown";
const intensidade = (info.analise_tecnica?.intensidade_db) ? parseFloat(info.analise_tecnica.intensidade_db) : -100;
const bpm = (info.analise_tecnica?.bpm) ? parseFloat(info.analise_tecnica.bpm) : 0;
// Extração segura das estrelas
let estrelas = 0;
if (info.analise_tecnica?.complexidade?.estrelas) {
estrelas = parseInt(info.analise_tecnica.complexidade.estrelas);
}
const estrelasHTML = gerarEstrelas(numeroEstrelas);
// --- 3. Injeção Visual (HTML) ---
// --- 2. Atualiza Dataset do HTML (Para filtros funcionarem) ---
card.dataset.genre = genero;
card.dataset.intensity = intensidade;
card.dataset.bpm_real = bpm;
card.dataset.stars = estrelas; // Importante para ordenação
// --- 3. Renderização Visual ---
const bpmContainer = card.querySelector('.bpm-container');
if (bpmContainer) {
// Cria container para as novas tags
const iaTagsDiv = document.createElement('div');
iaTagsDiv.className = "tags is-centered mt-1 mb-3";
iaTagsDiv.style.opacity = "0.9";
iaTagsDiv.style.flexDirection = "column"; // Organiza em linhas (estrelas em cima, tags em baixo)
iaTagsDiv.style.flexDirection = "column";
let htmlFinal = '';
// A) Adiciona as Estrelas
htmlFinal += `<div class="mb-1" style="font-size: 0.8rem;">${estrelasHTML}</div>`;
// Renderiza Estrelas (mesmo que seja 0, mostra estrelas vazias)
htmlFinal += `<div class="mb-1" style="font-size: 0.8rem;">${gerarEstrelas(estrelas)}</div>`;
// Container para as tags (Genero + Tom)
htmlFinal += '<div class="tags is-centered">';
// B) Tag de Gênero IA
if (info.analise_ia.genero_macro && info.analise_ia.genero_macro !== "Unknown") {
const genero = info.analise_ia.genero_macro;
// Tag Gênero
if (genero !== "Unknown") {
let colorClass = 'is-primary';
if(genero === 'Electronic') colorClass = 'is-info';
if(genero === 'Hip Hop') colorClass = 'is-warning';
if(genero === 'Rock') colorClass = 'is-danger';
htmlFinal += `<span class="tag ${colorClass} is-light is-rounded mr-1" style="font-size: 0.65rem;" title="Gênero IA">
htmlFinal += `<span class="tag ${colorClass} is-light is-rounded mr-1" style="font-size: 0.65rem;">
🤖 ${genero}
</span>`;
}
// C) Tag de Tom (Musical Key)
if (info.analise_tecnica.tom) {
// Tag Tom
if (info.analise_tecnica?.tom) {
const nota = info.analise_tecnica.tom;
const escala = info.analise_tecnica.escala === 'minor' ? 'm' : '';
htmlFinal += `<span class="tag is-white is-rounded" style="font-size: 0.65rem; border: 1px solid #ddd; color: #555;">
@ -419,70 +444,86 @@ document.addEventListener('DOMContentLoaded', () => {
</span>`;
}
htmlFinal += '</div>'; // Fecha container tags
htmlFinal += '</div>';
iaTagsDiv.innerHTML = htmlFinal;
bpmContainer.appendChild(iaTagsDiv);
}
} else {
// Se não achou dados
// Valores padrão se não achar dados
card.dataset.genre = "Outros";
card.dataset.intensity = -1;
card.dataset.intensity = -100;
card.dataset.stars = 0;
}
});
}
function createGenreButtons(data) {
// Extrai lista única de gêneros encontrados
const genres = new Set();
data.forEach(item => {
if(item.analise_ia && item.analise_ia.genero_macro && item.analise_ia.genero_macro !== "Unknown") {
if(item.analise_ia?.genero_macro && item.analise_ia.genero_macro !== "Unknown") {
genres.add(item.analise_ia.genero_macro);
}
});
// Cria botões HTML
const genresArray = Array.from(genres).sort();
genresArray.forEach(genre => {
const btn = document.createElement('button');
btn.className = 'button is-small is-rounded';
btn.className = 'button is-small is-rounded is-white'; // Começa branco
btn.textContent = genre;
btn.dataset.genre = genre;
btn.addEventListener('click', () => {
// Lógica de Ativação Visual
// UI Update
const allBtns = genreContainer.querySelectorAll('.button');
allBtns.forEach(b => {
b.classList.remove('is-active', 'is-info');
// Volta botões inativos para branco
if(b !== btn) b.classList.add('is-white');
b.classList.add('is-white');
});
btn.classList.remove('is-white');
btn.classList.add('is-active', 'is-info');
filterCards(genre);
// Logic Update
currentGenre = genre;
applyFilters();
});
genreContainer.appendChild(btn);
});
// Re-bind do botão "Todos"
// Botão Todos
const allBtn = genreContainer.querySelector('[data-genre="all"]');
allBtn.addEventListener('click', () => {
genreContainer.querySelectorAll('.button').forEach(b => b.classList.remove('is-active', 'is-info'));
genreContainer.querySelectorAll('.button').forEach(b => {
b.classList.remove('is-active', 'is-info');
b.classList.add('is-white');
});
allBtn.classList.remove('is-white');
allBtn.classList.add('is-active', 'is-info');
filterCards('all');
currentGenre = 'all';
applyFilters();
});
}
function filterCards(selectedGenre) {
cards.forEach(card => {
// Encontra a coluna pai para esconder tudo (layout grid)
// Lógica Unificada de Filtros
function applyFilters() {
cards.forEach(card => {
const column = card.closest('.project-column');
// 1. Checa Gênero
const cardGenre = card.dataset.genre;
const matchGenre = (currentGenre === 'all' || cardGenre === currentGenre);
if (selectedGenre === 'all' || cardGenre === selectedGenre) {
// 2. Checa Estrelas (Mínimo)
const cardStars = parseInt(card.dataset.stars) || 0;
let matchStars = true;
if (currentMinStars !== 'all') {
const min = parseInt(currentMinStars);
matchStars = (cardStars >= min);
}
// Exibe apenas se passar nos DOIS filtros
if (matchGenre && matchStars) {
column.style.display = 'block';
} else {
column.style.display = 'none';
@ -490,6 +531,14 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
// Evento do Dropdown de Estrelas
if(starFilterSelect) {
starFilterSelect.addEventListener('change', (e) => {
currentMinStars = e.target.value;
applyFilters();
});
}
// Ordenação
sortSelect.addEventListener('change', (e) => {
const criteria = e.target.value;
@ -499,25 +548,26 @@ document.addEventListener('DOMContentLoaded', () => {
const cardA = colA.querySelector('.project-card');
const cardB = colB.querySelector('.project-card');
// Valores padrão caso não tenha dados
const intensityA = parseFloat(cardA.dataset.intensity) || 0;
const intensityB = parseFloat(cardB.dataset.intensity) || 0;
const intensityA = parseFloat(cardA.dataset.intensity) || -100;
const intensityB = parseFloat(cardB.dataset.intensity) || -100;
const bpmA = parseFloat(cardA.dataset.bpm_real) || 0;
const bpmB = parseFloat(cardB.dataset.bpm_real) || 0;
const starsA = parseInt(cardA.dataset.stars) || 0;
const starsB = parseInt(cardB.dataset.stars) || 0;
// Lógica de comparação
if (criteria === 'intensity_desc') return intensityB - intensityA;
if (criteria === 'intensity_asc') return intensityA - intensityB;
if (criteria === 'bpm_desc') return bpmB - bpmA;
if (criteria === 'bpm_asc') return bpmA - bpmB;
// Novas ordenações
if (criteria === 'stars_desc') return starsB - starsA;
if (criteria === 'stars_asc') return starsA - starsB;
// Padrão (Ordem alfabética do título original)
const titleA = cardA.dataset.title.toLowerCase();
const titleB = cardB.dataset.title.toLowerCase();
return titleA.localeCompare(titleB);
});
// Re-anexa os elementos na nova ordem
columns.forEach(col => projectsContainer.appendChild(col));
});