NezQuest / modified.html
OddNez's picture
Upload 72 files
613eb13 verified
<!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;
}
/* Custom Scrollbar */
::-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>
<!-- TOTAL MODIFIED - Full Width Progress Bar -->
<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();
// Modified cartridges data loaded from database/nes-modified.json
let modifiedData = [];
// Extended info loaded from JSON
let extendedInfo = {};
let currentFilter = 'all';
// Load modified cartridges data from JSON
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);
}
}
// Load extended info from JSON
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;
// Merge extended info into modifiedData
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());
// Support both old single modificationType and new modificationTypes array
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`;
// Support both old single modificationType and new modificationTypes array
const types = item.modificationTypes || [item.modificationType];
const primaryType = types[0]; // Use first type for border color
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')})`;
// Use extended info for display if available
const description = item.description || item.notes;
const genre = item.genre || item.modificationDetail;
// Generate tags for all modification types
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>
`;
// Add click handler for the card (not the link)
div.onclick = (e) => {
if (e.target.tagName !== 'A') {
window.location.href = `game.html?id=${item.id}&category=modified`;
}
};
list.appendChild(div);
});
// Re-init feather icons for new elements
if (typeof feather !== 'undefined') {
feather.replace();
}
}
function setFilter(filter) {
currentFilter = filter;
// Reset all buttons to inactive state (black bg, gray text)
document.querySelectorAll('button[id^="filter-"]').forEach(btn => {
btn.className = 'px-3 py-1 bg-black border-2 rounded hover:bg-nes-dark transition-colors';
// Preserve the specific border/text color classes based on button type
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');
}
});
// Set active button (blue bg, white text)
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;
// Count games that include each modification type (supporting both old and new format)
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;
// Update counts
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;
// Update progress bar (always full since this is the total, or could show % of max possible)
const progressBar = document.getElementById('modified-progress-bar');
if (progressBar) {
// Animate to full width since this represents 100% of modified collection
setTimeout(() => {
progressBar.style.width = '100%';
}, 100);
}
}
document.getElementById('search').addEventListener('input', (e) => renderModified(e.target.value));
// Add click event listeners to filter buttons
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'));
// Init
window.onload = async () => {
feather.replace();
await loadModifiedData();
await loadExtendedInfo();
updateStats();
setFilter('all');
};
</script>
</body>
</html>