| <!DOCTYPE html> |
| <html lang="en"> |
|
|
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>NezQuest - Modified Cartridges</title> |
| <link rel="stylesheet" href="style.css"> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| 'nes-red': '#e71d36', |
| 'nes-blue': '#2ec4b6', |
| 'nes-yellow': '#ff9f1c', |
| 'nes-dark': '#011627', |
| }, |
| fontFamily: { |
| 'press-start': ['"Press Start 2P"', 'cursive'], |
| }, |
| } |
| } |
| } |
| </script> |
| <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet"> |
| <style> |
| |
| body { |
| background-color: var(--nes-dark); |
| font-family: 'Press Start 2P', cursive; |
| background-image: |
| linear-gradient(rgba(46, 196, 182, 0.05) 1px, transparent 1px), |
| linear-gradient(90px, rgba(46, 196, 182, 0.05) 1px, transparent 1px); |
| background-size: 20px 20px; |
| color: white; |
| line-height: 1.6; |
| } |
| |
| .game-card { |
| background: rgba(0, 0, 0, 0.6); |
| border-left: 4px solid transparent; |
| transition: all 0.2s ease; |
| } |
| |
| .game-card:hover { |
| background: rgba(255, 255, 255, 0.05); |
| transform: translateX(5px); |
| } |
| |
| .mod-tag { |
| font-size: 0.6rem; |
| padding: 2px 6px; |
| border: 1px solid; |
| text-transform: uppercase; |
| } |
| |
| .text-xxs { |
| font-size: 0.5rem; |
| } |
| |
| |
| ::-webkit-scrollbar { |
| width: 8px; |
| } |
| |
| ::-webkit-scrollbar-track { |
| background: var(--nes-dark); |
| } |
| |
| ::-webkit-scrollbar-thumb { |
| background: var(--nes-blue); |
| border-radius: 4px; |
| } |
| </style> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| 'nes-red': '#e71d36', |
| 'nes-blue': '#2ec4b6', |
| 'nes-yellow': '#ff9f1c', |
| 'nes-dark': '#011627', |
| } |
| } |
| } |
| } |
| </script> |
| </head> |
|
|
| <body class="bg-nes-dark text-white font-press-start bg-grid min-h-screen scanlines"> |
| <custom-header></custom-header> |
| <div id="login-modal" class="fixed inset-0 bg-black bg-opacity-80 z-50 flex items-center justify-center hidden"> |
| <div class="bg-nes-dark p-6 rounded-lg border-4 border-nes-yellow max-w-md w-full"> |
| <h2 class="text-nes-yellow text-xl mb-4 text-center">Admin Login</h2> |
| <input type="password" id="login-password" placeholder="Password" class="w-full p-2 mb-4 bg-black text-white border-2 border-nes-blue rounded"> |
| <button id="login-button" class="w-full p-2 bg-nes-red text-white rounded hover:bg-red-700">Login</button> |
| </div> |
| </div> |
|
|
| <main class="container mx-auto px-4 py-8 max-w-4xl"> |
| <h1 class="text-2xl text-center mb-3 text-orange-400">Modified Cartridges</h1> |
| <p class="text-xs text-center text-gray-400 mb-6">Cartridges with replacement shells, labels, or other |
| non-original parts</p> |
|
|
| |
| <div class="bg-black bg-opacity-90 border-4 border-nes-yellow p-4 mb-4"> |
| <div class="flex justify-between items-center mb-2"> |
| <h2 class="text-nes-yellow text-xs">TOTAL MODIFIED</h2> |
| <span id="modified-total-count" class="text-nes-yellow text-lg">0</span> |
| </div> |
| <div class="w-full bg-gray-800 h-4 border-2 border-gray-700 rounded overflow-hidden relative"> |
| <div id="modified-progress-bar" class="h-full bg-nes-yellow transition-all duration-500" style="width: 0%"></div> |
| </div> |
| </div> |
|
|
| <div class="grid grid-cols-3 gap-4 mb-6"> |
| <div class="bg-black bg-opacity-90 border-4 border-nes-blue p-4 flex flex-col justify-center items-center"> |
| <span class="text-nes-blue text-xs mb-1">REPLACED SHELLS</span> |
| <span id="shell-replacement-count" class="text-2xl text-nes-blue">0</span> |
| </div> |
|
|
| <div class="bg-black bg-opacity-90 border-4 border-nes-red p-4 flex flex-col justify-center items-center"> |
| <span class="text-nes-red text-xs mb-1">REPLACED LABELS</span> |
| <span id="label-replacement-count" class="text-2xl text-nes-red">0</span> |
| </div> |
| <div class="bg-black bg-opacity-90 border-4 border-nes-blue p-4 flex flex-col justify-center items-center"> |
| <span class="text-nes-blue text-xs mb-1">REPLACED BOARDS</span> |
| <span id="board-replacement-count" class="text-2xl text-nes-blue">0</span> |
| </div> |
| </div> |
|
|
| <div class="mb-4 flex flex-col sm:flex-row justify-between items-center gap-2"> |
| <div class="flex gap-2"> |
| <button id="filter-all" class="px-3 py-1 bg-nes-blue text-white border-2 border-nes-blue rounded hover:bg-nes-dark transition-colors"> |
| ALL |
| </button> |
| <button id="filter-shell" class="px-3 py-1 bg-black text-nes-yellow border-2 border-nes-yellow rounded hover:bg-nes-dark transition-colors"> |
| SHELL |
| </button> |
| <button id="filter-label" class="px-3 py-1 bg-black text-nes-red border-2 border-nes-red rounded hover:bg-nes-dark transition-colors"> |
| LABEL |
| </button> |
| <button id="filter-board" class="px-3 py-1 bg-black text-nes-blue border-2 border-nes-blue rounded hover:bg-nes-dark transition-colors"> |
| BOARD |
| </button> |
| </div> |
| <div class="flex gap-2 items-center"> |
| <input type="text" id="search" placeholder="Search modified carts..." class="px-3 py-1 bg-black text-white border-2 border-nes-blue rounded"> |
| </div> |
| </div> |
|
|
| <div id="games-list" class="space-y-3"> |
| </div> |
| </main> |
|
|
| <custom-footer></custom-footer> |
|
|
| <script src="components/header.js"></script> |
| <script src="components/footer.js"></script> |
| <script> |
| feather.replace(); |
| |
| |
| let modifiedData = []; |
| |
| |
| let extendedInfo = {}; |
| |
| let currentFilter = 'all'; |
| |
| |
| async function loadModifiedData() { |
| try { |
| const response = await fetch('database/nes-modified.json'); |
| if (response.ok) { |
| modifiedData = await response.json(); |
| } |
| } catch (error) { |
| console.log('Modified data not available:', error); |
| } |
| } |
| |
| |
| async function loadExtendedInfo() { |
| try { |
| const response = await fetch('database/modified-extended-info.json'); |
| if (response.ok) { |
| const data = await response.json(); |
| if (data.modified) { |
| extendedInfo = data.modified; |
| |
| modifiedData = modifiedData.map(item => { |
| const ext = extendedInfo[item.id.toString()]; |
| if (ext) { |
| return { ...item, ...ext }; |
| } |
| return item; |
| }); |
| renderModified(); |
| } |
| } |
| } catch (error) { |
| console.log('Extended info not available:', error); |
| } |
| } |
| |
| function renderModified(searchTerm = '') { |
| const list = document.getElementById('games-list'); |
| const filtered = modifiedData.filter(item => { |
| const matchesSearch = item.title.toLowerCase().includes(searchTerm.toLowerCase()); |
| |
| const types = item.modificationTypes || [item.modificationType]; |
| const matchesFilter = currentFilter === 'all' || types.includes(currentFilter); |
| return matchesSearch && matchesFilter; |
| }); |
| |
| list.innerHTML = filtered.length ? '' : '<div class="text-center text-gray-600 text-[10px] py-10">NO DATA FOUND</div>'; |
| |
| filtered.sort((a, b) => a.title.localeCompare(b.title)).forEach(item => { |
| const div = document.createElement('div'); |
| div.className = `game-card p-4 border-2 border-gray-800 flex flex-col md:flex-row justify-between items-start md:items-center gap-4 cursor-pointer`; |
| |
| |
| const types = item.modificationTypes || [item.modificationType]; |
| const primaryType = types[0]; |
| |
| let colorClass = primaryType === 'shell' ? 'text-nes-yellow' : |
| primaryType === 'label' ? 'text-nes-red' : 'text-blue-400'; |
| |
| div.style.borderLeftColor = `var(--nes-${primaryType === 'board' ? 'blue' : (primaryType === 'shell' ? 'yellow' : 'red')})`; |
| |
| |
| const description = item.description || item.notes; |
| const genre = item.genre || item.modificationDetail; |
| |
| |
| const typeTagsHtml = types.map(type => { |
| const tagColorClass = type === 'shell' ? 'text-nes-yellow border-nes-yellow' : |
| type === 'label' ? 'text-nes-red border-nes-red' : |
| 'text-blue-400 border-blue-400'; |
| return `<span class="mod-tag ${tagColorClass} mr-1">${type}</span>`; |
| }).join(''); |
| |
| div.innerHTML = ` |
| <div class="flex-1"> |
| <div class="flex items-center gap-2 mb-1"> |
| <a href="game.html?id=${item.id}&category=modified" class="text-sm text-nes-yellow hover:underline cursor-pointer">${item.title}</a> |
| </div> |
| <div class="text-xxs text-gray-500 uppercase">${item.year} • ${item.region}</div> |
| <div class="text-[9px] text-gray-400 mt-2 italic">${description}</div> |
| ${item.boxArtFront ? `<div class="mt-2 text-[8px] text-nes-blue flex items-center gap-1"><i data-feather="image" class="w-3 h-3"></i> Box Art Available</div>` : ''} |
| </div> |
| <div class="flex flex-col items-start md:items-end gap-2 w-full md:w-auto border-t md:border-t-0 border-gray-800 pt-3 md:pt-0"> |
| <div class="flex flex-wrap justify-end gap-1">${typeTagsHtml}</div> |
| <span class="text-[8px] text-gray-500 text-right uppercase tracking-tighter">${item.modificationDetail}</span> |
| ${item.genre && item.genre !== item.modificationDetail ? `<span class="text-[8px] text-nes-blue">${item.genre}</span>` : ''} |
| </div> |
| `; |
| |
| |
| div.onclick = (e) => { |
| if (e.target.tagName !== 'A') { |
| window.location.href = `game.html?id=${item.id}&category=modified`; |
| } |
| }; |
| |
| list.appendChild(div); |
| }); |
| |
| |
| if (typeof feather !== 'undefined') { |
| feather.replace(); |
| } |
| } |
| |
| function setFilter(filter) { |
| currentFilter = filter; |
| |
| |
| document.querySelectorAll('button[id^="filter-"]').forEach(btn => { |
| btn.className = 'px-3 py-1 bg-black border-2 rounded hover:bg-nes-dark transition-colors'; |
| |
| if (btn.id === 'filter-all') { |
| btn.classList.add('border-nes-blue', 'text-nes-blue'); |
| } else if (btn.id === 'filter-shell') { |
| btn.classList.add('border-nes-yellow', 'text-nes-yellow'); |
| } else if (btn.id === 'filter-label') { |
| btn.classList.add('border-nes-red', 'text-nes-red'); |
| } else if (btn.id === 'filter-board') { |
| btn.classList.add('border-nes-blue', 'text-nes-blue'); |
| } |
| }); |
| |
| |
| const active = document.getElementById(`filter-${filter}`); |
| active.className = 'px-3 py-1 bg-nes-blue text-white border-2 border-nes-blue rounded transition-colors'; |
| |
| renderModified(document.getElementById('search').value); |
| } |
| |
| function updateStats() { |
| const total = modifiedData.length; |
| |
| |
| const shellCount = modifiedData.filter(i => { |
| const types = i.modificationTypes || [i.modificationType]; |
| return types.includes('shell'); |
| }).length; |
| const labelCount = modifiedData.filter(i => { |
| const types = i.modificationTypes || [i.modificationType]; |
| return types.includes('label'); |
| }).length; |
| const boardCount = modifiedData.filter(i => { |
| const types = i.modificationTypes || [i.modificationType]; |
| return types.includes('board'); |
| }).length; |
| |
| |
| document.getElementById('modified-total-count').textContent = total; |
| document.getElementById('shell-replacement-count').textContent = shellCount; |
| document.getElementById('label-replacement-count').textContent = labelCount; |
| document.getElementById('board-replacement-count').textContent = boardCount; |
| |
| |
| const progressBar = document.getElementById('modified-progress-bar'); |
| if (progressBar) { |
| |
| setTimeout(() => { |
| progressBar.style.width = '100%'; |
| }, 100); |
| } |
| } |
| |
| document.getElementById('search').addEventListener('input', (e) => renderModified(e.target.value)); |
| |
| |
| document.getElementById('filter-all').addEventListener('click', () => setFilter('all')); |
| document.getElementById('filter-shell').addEventListener('click', () => setFilter('shell')); |
| document.getElementById('filter-label').addEventListener('click', () => setFilter('label')); |
| document.getElementById('filter-board').addEventListener('click', () => setFilter('board')); |
| |
| |
| window.onload = async () => { |
| feather.replace(); |
| await loadModifiedData(); |
| await loadExtendedInfo(); |
| updateStats(); |
| setFilter('all'); |
| }; |
| </script> |
| </body> |
|
|
| </html> |