mirror of
https://github.com/Cametendo/devil-fruit-encyclopedia.git
synced 2026-09-14 00:09:47 +02:00
Replaces the 4-card quickstart mockup with a data-driven wiki covering the 100 most well-known One Piece characters and every Devil Fruit they carry (57 fruits total), each with its own linked detail page. - js/characters-data.js: 100 character profiles (stats, bio, abilities) - js/fruits-data.js: 57 Devil Fruit entries (history, powers, strengths/ weaknesses, full user lineage) cross-linked to character ids - index.html + main.js: searchable/filterable character grid - character.html + character.js: character detail template driven by ?id= - fruits.html + fruits.js: searchable Devil Fruit index with type filter - fruit.html + fruit.js: Devil Fruit detail template driven by ?id= - css/styles.css: new wanted-poster/ocean-navy design system, responsive - Fixed docker-compose volume mount (previously pointed at a nonexistent ./data directory) and rewrote README for the new structure Verified with a headless browser: all four page types render without script errors, search/filter interactions work, and every character- fruit cross-reference resolves correctly.
86 lines
3.3 KiB
JavaScript
86 lines
3.3 KiB
JavaScript
/* Character detail page: renders a full profile from ?id= against CHARACTERS/FRUITS. */
|
|
|
|
(function () {
|
|
const root = document.getElementById('character-content');
|
|
const id = qs('id');
|
|
const character = id ? findCharacter(id) : null;
|
|
|
|
if (!character) {
|
|
root.innerHTML = `
|
|
<div class="empty-state">
|
|
Character not found. <a href="index.html">Return to the character index</a>.
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
document.title = `${character.name} — One Piece Wiki`;
|
|
const metaDesc = document.querySelector('meta[name="description"]');
|
|
if (metaDesc) metaDesc.setAttribute('content', `${character.name}${character.epithet ? ' "' + character.epithet + '"' : ''} — profile, abilities, and Devil Fruit on the One Piece Wiki.`);
|
|
|
|
const accent = accentFor(character);
|
|
const fruit = character.devilFruit ? findFruit(character.devilFruit) : null;
|
|
const fruitRelation = fruit ? fruit.users.find((u) => u.id === character.id) : null;
|
|
|
|
const statRows = [
|
|
['Affiliation', character.affiliation],
|
|
['Role', character.role],
|
|
['Origin', character.origin],
|
|
['Age', character.age ?? 'Unknown'],
|
|
['Height', character.height || 'Unknown'],
|
|
['Birthday', character.birthday || 'Unknown'],
|
|
['Status', character.status || 'Unknown'],
|
|
['First Appearance', character.firstAppearance || 'Unknown'],
|
|
];
|
|
|
|
root.innerHTML = `
|
|
<section class="profile-header" style="--accent:${accent}">
|
|
<div class="profile-avatar">${initialFor(character.name)}</div>
|
|
<div>
|
|
<p class="profile-eyebrow">${escapeHtml(character.affiliation)} · ${escapeHtml(character.role)}</p>
|
|
<h1>${escapeHtml(character.name)}</h1>
|
|
<p class="profile-epithet">${character.epithet ? '"' + escapeHtml(character.epithet) + '"' : ''}</p>
|
|
<div class="profile-badges">
|
|
<span class="badge ${statusBadgeClass(character.status)}">${escapeHtml(character.status || 'Unknown')}</span>
|
|
<span class="badge badge--bounty">${character.bounty ? formatBounty(character.bounty) : 'Bounty Unknown'}</span>
|
|
${fruit ? `<span class="badge" data-cat="${escapeHtml(fruit.category)}" style="color:var(--cat-color)">${escapeHtml(fruit.category)} User</span>` : ''}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="profile-layout">
|
|
<div class="profile-main">
|
|
${fruit ? `
|
|
<a class="fruit-callout" href="${fruitLink(fruit.id)}">
|
|
<div class="fruit-callout__icon">${fruit.icon}</div>
|
|
<div>
|
|
<p class="fruit-callout__label">${escapeHtml(fruitRelation ? fruitRelation.status : 'Devil Fruit')}</p>
|
|
<h3>${escapeHtml(fruit.name)}</h3>
|
|
<p>${escapeHtml(fruit.tagline)}</p>
|
|
</div>
|
|
</a>
|
|
` : ''}
|
|
|
|
<div class="card">
|
|
<h2>Biography</h2>
|
|
<p>${escapeHtml(character.bio)}</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Abilities & Haki</h2>
|
|
<ul class="ability-list">
|
|
${character.abilities.map((a) => `<li>${escapeHtml(a)}</li>`).join('')}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<aside class="profile-stats card">
|
|
<h2>Profile</h2>
|
|
<dl class="stat-list">
|
|
${statRows.map(([label, value]) => `<div><dt>${escapeHtml(label)}</dt><dd>${escapeHtml(value)}</dd></div>`).join('')}
|
|
</dl>
|
|
</aside>
|
|
</div>
|
|
`;
|
|
})();
|