Aplicaciones Web Orientadas a Servicios ITIID-05

Práctica Guiada: Mine-API - 2


script.js

// CONFIGURACIÓN
const YOUTUBE_API_KEY = "";
const BASE_API_URL = "https://mine-api-jade.vercel.app/api";

// Estado global de la vista actual
let currentData = [];
let currentCategory = 'personajes';

// Inicializa la app con la categoría por defecto
async function init() {
    await fetchData('personajes');
}

// Cambia de categoría, actualiza UI y recarga datos
async function changeCategory(category) {
    currentCategory = category;

    document.querySelectorAll('.mc-nav-btn').forEach(btn => btn.classList.remove('active'));
    const activeBtn = document.getElementById(`btn-${category.replace('/', '-')}`);
    if (activeBtn) activeBtn.classList.add('active');

    await fetchData(category);
    closeSidebar();
}

// Consulta datos en la API y normaliza la respuesta
async function fetchData(endpoint) {
    const loading = document.getElementById('loading');
    const grid = document.getElementById('contentGrid');

    loading.classList.remove('hidden');
    grid.innerHTML = '';

    try {
        const response = await fetch(`${BASE_API_URL}/${endpoint}`);
        const data = await response.json();

        if (Array.isArray(data)) {
            currentData = data;
        } else if (data && typeof data === 'object') {
            const possibleArray = Object.values(data).find(val => Array.isArray(val));
            currentData = possibleArray || [];
        } else {
            currentData = [];
        }

        loading.classList.add('hidden');
        renderContent(currentData);
    } catch (error) {
        loading.innerText = "Error: No se pudo conectar con el servidor.";
        console.error("Error fetching data:", error);
    }
}

// Dibuja las tarjetas del listado principal
function renderContent(data) {
    const grid = document.getElementById('contentGrid');
    grid.innerHTML = '';

    if (!Array.isArray(data) || data.length === 0) {
        grid.innerHTML = '<p class="col-span-full text-center text-xl text-red-400">No se encontraron resultados o la API devolvió un formato inesperado.</p>';
        return;
    }

    data.forEach(item => {
        if (!item) return;

        const card = document.createElement('div');
        card.className = 'mc-card p-4 flex flex-col items-center cursor-pointer text-center';
        card.onclick = () => showDetails(item);

        const name = item.nombre || item.name || "Desconocido";
        const imgUrl = item.imagen || item.image || 'https://via.placeholder.com/150?text=Minecraft';

        card.innerHTML = `
            <img src="${imgUrl}" alt="${name}" class="w-24 h-24 mb-3 object-contain">
            <span class="text-2xl leading-none uppercase text-white">${name}</span>
            <span class="badge bg-black/40 text-gray-400 uppercase text-xs">${currentCategory.split('/')[0]}</span>
        `;
        grid.appendChild(card);
    });
}

// Muestra el detalle del elemento seleccionado
async function showDetails(item) {
    const sidebar = document.getElementById('sidebar');
    sidebar.classList.remove('hidden');

    const name = item.nombre || item.name || "Desconocido";
    const img = item.imagen || item.image || 'https://via.placeholder.com/150?text=Minecraft';
    const desc = item.descripcion || item.description || "Sin descripción disponible.";

    document.getElementById('detailName').innerText = name;
    document.getElementById('detailImg').src = img;
    document.getElementById('detailDesc').innerText = desc;

    const statsDiv = document.getElementById('detailStats');
    statsDiv.innerHTML = '';
    if (item.vida) statsDiv.innerHTML += `<div>❤ Vida: ${item.vida}</div>`;
    if (item.comportamiento) statsDiv.innerHTML += `<div>⚙ Comportamiento: ${item.comportamiento}</div>`;
    if (item.clima) statsDiv.innerHTML += `<div>☁ Clima: ${item.clima}</div>`;

    if (window.innerWidth < 1024) {
        sidebar.scrollIntoView({ behavior: 'smooth' });
    }

    fetchVideos(name);
}

// Oculta el panel lateral de detalle
function closeSidebar() {
    document.getElementById('sidebar').classList.add('hidden');
}

// Busca videos relacionados en YouTube para el item activo
async function fetchVideos(queryTerm) {
    const videoResults = document.getElementById('videoResults');
    videoResults.innerHTML = '<p class="text-yellow-400 text-xl">Buscando guías...</p>';

    if (!YOUTUBE_API_KEY) {
        videoResults.innerHTML = '<p class="text-red-400 text-sm italic">Ingresa tu YouTube API Key en el código para habilitar videos.</p>';
        return;
    }

    try {
        const categoryContext = currentCategory.includes('biomas') ? 'bioma' : '';
        const fullQuery = encodeURIComponent(`minecraft ${categoryContext} ${queryTerm} guide`);
        const url = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=2&q=${fullQuery}&type=video&key=${YOUTUBE_API_KEY}`;

        const response = await fetch(url);
        const data = await response.json();

        if (data.items && data.items.length > 0) {
            videoResults.innerHTML = '';

            // ✅ FIX: usar el origin sin encodeURIComponent, y manejar el caso file://
            const currentOrigin = (window.location.origin && window.location.origin !== 'null')
                ? window.location.origin
                : 'https://www.youtube.com';

            data.items.forEach(video => {
                const videoId = video.id.videoId;
                if (videoId) {
                    const div = document.createElement('div');
                    div.className = 'video-container mb-4';
                    // ✅ FIX: origin sin encodeURIComponent para que YouTube lo valide correctamente
                    const embedUrl = `https://www.youtube.com/embed/${videoId}?enablejsapi=1&origin=${currentOrigin}`;

                    div.innerHTML = `<iframe 
                        id="player-${videoId}"
                        type="text/html"
                        src="${embedUrl}" 
                        frameborder="0" 
                        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" 
                        allowfullscreen></iframe>`;
                    videoResults.appendChild(div);
                }
            });
        } else {
            videoResults.innerHTML = '<p class="text-gray-500">No se encontraron guías.</p>';
        }
    } catch (error) {
        console.error("YouTube API Error:", error);
        videoResults.innerHTML = '<p class="text-red-500 text-sm">Error en YouTube API. Revisa tu cuota o clave.</p>';
    }
}

// Filtro local por nombre dentro de la categoría actual
document.getElementById('searchInput').addEventListener('input', (e) => {
    const term = e.target.value.toLowerCase();
    const filtered = currentData.filter(item => {
        if (!item) return false;
        const name = (item.nombre || item.name || "").toLowerCase();
        return name.includes(term);
    });
    renderContent(filtered);
});

// Punto de entrada
window.onload = init;