clean up site

This commit is contained in:
nickp
2025-10-06 19:38:59 +11:00
parent 8de517f52e
commit acebe92a6a
7 changed files with 883 additions and 127 deletions

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }} - Document Explorer</title>
<title>{{ title }} - Epstein Archive</title>
<style>
* {
margin: 0;
@@ -275,12 +275,430 @@
outline: none;
border-color: #3498db;
}
/* Alphabet Navigation - used by entity pages */
.alphabet-nav {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 1.5rem 0;
padding: 1rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.letter-btn {
padding: 0.5rem 0.75rem;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 4px;
font-weight: 500;
transition: all 0.2s;
}
.letter-btn:hover {
background: #f0f0f0;
border-color: #3498db;
}
.letter-btn.active {
background: #3498db;
color: white;
border-color: #3498db;
}
/* Entity Items (collapsible) - used by people/organizations/locations */
.entity-item {
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
margin-bottom: 0.75rem;
transition: all 0.2s;
}
.entity-item:hover {
border-color: #3498db;
box-shadow: 0 2px 8px rgba(52, 152, 219, 0.1);
}
.entity-summary {
padding: 1rem 1.5rem;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.1rem;
user-select: none;
}
.entity-summary::-webkit-details-marker {
display: none;
}
.entity-summary::before {
content: '▶';
margin-right: 0.75rem;
color: #666;
transition: transform 0.2s;
}
details[open] .entity-summary::before {
transform: rotate(90deg);
}
.entity-name {
font-weight: 600;
color: #2c3e50;
}
.entity-count {
font-size: 0.9rem;
color: #666;
background: #f0f0f0;
padding: 0.25rem 0.75rem;
border-radius: 12px;
}
.entity-content {
padding: 0 1.5rem 1.5rem 3.25rem;
display: grid;
gap: 0.75rem;
}
/* Compact Document Cards - used in entity pages */
.document-card-compact {
padding: 0.75rem;
background: #f8f9fa;
border-radius: 4px;
border-left: 3px solid #3498db;
}
.document-card-compact:hover {
background: #e9ecef;
}
.doc-link {
color: #3498db;
text-decoration: none;
font-size: 1rem;
}
.doc-link:hover {
text-decoration: underline;
}
.meta-compact {
font-size: 0.85rem;
color: #666;
margin-top: 0.25rem;
}
/* Pagination Controls - used by all-documents page */
.pagination-info {
margin: 1rem 0;
text-align: center;
color: #666;
}
.pagination-controls {
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
margin: 2rem 0;
flex-wrap: wrap;
}
.page-btn {
padding: 0.5rem 1rem;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 4px;
font-weight: 500;
transition: all 0.2s;
}
.page-btn:hover:not(:disabled) {
background: #f0f0f0;
border-color: #3498db;
}
.page-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.page-num {
padding: 0.5rem 0.75rem;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s;
min-width: 40px;
text-align: center;
}
.page-num:hover {
background: #f0f0f0;
border-color: #3498db;
}
.page-num.active {
background: #3498db;
color: white;
border-color: #3498db;
}
/* Document Detail Page Layout */
.doc-layout {
display: grid;
grid-template-columns: 250px 1fr;
gap: 2rem;
margin-top: 2rem;
}
.doc-sidebar {
position: sticky;
top: 20px;
height: fit-content;
}
.doc-main {
max-width: 800px;
}
.doc-toc {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 1.5rem;
}
.doc-toc h3 {
font-size: 0.9rem;
text-transform: uppercase;
color: #666;
margin-bottom: 1rem;
}
.toc-list {
list-style: none;
}
.toc-list li {
margin-bottom: 0.5rem;
}
.toc-list a {
color: #3498db;
text-decoration: none;
font-size: 0.9rem;
}
.toc-list a:hover {
text-decoration: underline;
}
.doc-entities {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.doc-entities h3 {
font-size: 0.9rem;
text-transform: uppercase;
color: #666;
margin-bottom: 1rem;
}
.entity-section {
margin-bottom: 1.5rem;
}
.entity-section h4 {
font-size: 0.85rem;
color: #888;
margin-bottom: 0.5rem;
}
.entity-section ul {
list-style: none;
margin: 0;
padding: 0;
}
.entity-section li {
padding: 0.25rem 0;
font-size: 0.9rem;
}
.entity-section a {
color: #3498db;
text-decoration: none;
}
.entity-section a:hover {
text-decoration: underline;
}
.metadata-box {
background: #f8f9fa;
padding: 1.5rem;
border-radius: 8px;
margin-bottom: 2rem;
border-left: 4px solid #3498db;
}
.metadata-item strong {
display: block;
font-size: 0.75rem;
text-transform: uppercase;
color: #666;
margin-bottom: 0.25rem;
}
.full-text-container {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 2rem;
}
.full-text-container h2 {
margin-bottom: 1.5rem;
color: #2c3e50;
}
.full-text-container .full-text {
font-family: Georgia, serif;
font-size: 1.05rem;
line-height: 1.8;
white-space: pre-wrap;
color: #333;
}
.page-details {
margin-top: 2rem;
}
.page-detail {
margin-bottom: 1rem;
}
.page-summary {
padding: 1rem 1.5rem;
background: #f8f9fa;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
}
.page-summary:hover {
background: #e9ecef;
}
.page-content {
padding: 1.5rem;
background: white;
border: 1px solid #e0e0e0;
border-top: none;
border-radius: 0 0 4px 4px;
}
/* Responsive Design */
@media (max-width: 968px) {
.doc-layout {
grid-template-columns: 1fr;
}
.doc-sidebar {
position: static;
}
nav .container {
flex-direction: column;
gap: 1rem;
}
.browse-grid {
grid-template-columns: 1fr;
}
.stats {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 600px) {
.hero h1 {
font-size: 2rem;
}
.stats {
grid-template-columns: 1fr;
}
.alphabet-nav {
justify-content: center;
}
.entity-summary {
font-size: 1rem;
padding: 0.75rem 1rem;
}
.entity-content {
padding: 0 1rem 1rem 2.5rem;
}
}
/* Typography improvements */
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
line-height: 1.3;
}
h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
}
h2 {
font-size: 2rem;
margin-bottom: 0.75rem;
}
h3 {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
h4 {
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
p {
margin-bottom: 1rem;
}
a {
transition: all 0.2s;
}
</style>
</head>
<body>
<nav>
<div class="container">
<a href="/" style="font-weight: bold; font-size: 1.2rem;">Document Archive</a>
<a href="/" style="font-weight: bold; font-size: 1.2rem;">Epstein Archive</a>
<div>
<a href="/people/">People</a>
<a href="/organizations/">Organizations</a>

View File

@@ -10,9 +10,13 @@ title: All Documents
<input type="text" id="search" placeholder="Search documents...">
</div>
<div class="pagination-info" style="margin: 1rem 0; text-align: center; color: #666;">
Showing <span id="showing-start">1</span>-<span id="showing-end">50</span> of <span id="total-count">{{ documents.length }}</span>
</div>
<div id="results">
{% for doc in documents %}
<div class="document-card">
<div class="document-card" data-index="{{ loop.index0 }}">
<h4>Document {{ doc.document_number }}</h4>
<div class="meta">
{% if doc.document_metadata.document_type %}Type: {{ doc.document_metadata.document_type }} | {% endif %}
@@ -33,17 +37,129 @@ title: All Documents
{% endfor %}
</div>
<div class="pagination-controls" style="display: flex; justify-content: center; align-items: center; gap: 1rem; margin: 2rem 0; flex-wrap: wrap;">
<button id="prev-btn" class="page-btn">← Previous</button>
<div id="page-numbers" style="display: flex; gap: 0.5rem; flex-wrap: wrap;"></div>
<button id="next-btn" class="page-btn">Next →</button>
</div>
<script>
const search = document.getElementById('search');
const results = document.getElementById('results');
const allCards = Array.from(results.querySelectorAll('.document-card'));
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
const pageNumbersContainer = document.getElementById('page-numbers');
const showingStart = document.getElementById('showing-start');
const showingEnd = document.getElementById('showing-end');
const totalCount = document.getElementById('total-count');
const itemsPerPage = 50;
let currentPage = 1;
let filteredCards = allCards;
function updatePagination() {
const totalPages = Math.ceil(filteredCards.length / itemsPerPage);
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = Math.min(startIndex + itemsPerPage, filteredCards.length);
// Hide all cards
allCards.forEach(card => card.style.display = 'none');
// Show current page cards
filteredCards.slice(startIndex, endIndex).forEach(card => card.style.display = 'block');
// Update info
showingStart.textContent = filteredCards.length > 0 ? startIndex + 1 : 0;
showingEnd.textContent = endIndex;
totalCount.textContent = filteredCards.length;
// Update buttons
prevBtn.disabled = currentPage === 1;
nextBtn.disabled = currentPage === totalPages || filteredCards.length === 0;
// Update page numbers
pageNumbersContainer.innerHTML = '';
const maxPageButtons = 7;
let startPage = Math.max(1, currentPage - Math.floor(maxPageButtons / 2));
let endPage = Math.min(totalPages, startPage + maxPageButtons - 1);
if (endPage - startPage < maxPageButtons - 1) {
startPage = Math.max(1, endPage - maxPageButtons + 1);
}
if (startPage > 1) {
const btn = createPageButton(1);
pageNumbersContainer.appendChild(btn);
if (startPage > 2) {
const ellipsis = document.createElement('span');
ellipsis.textContent = '...';
ellipsis.style.padding = '0.5rem';
pageNumbersContainer.appendChild(ellipsis);
}
}
for (let i = startPage; i <= endPage; i++) {
const btn = createPageButton(i);
pageNumbersContainer.appendChild(btn);
}
if (endPage < totalPages) {
if (endPage < totalPages - 1) {
const ellipsis = document.createElement('span');
ellipsis.textContent = '...';
ellipsis.style.padding = '0.5rem';
pageNumbersContainer.appendChild(ellipsis);
}
const btn = createPageButton(totalPages);
pageNumbersContainer.appendChild(btn);
}
}
function createPageButton(page) {
const btn = document.createElement('button');
btn.textContent = page;
btn.className = 'page-num';
if (page === currentPage) {
btn.classList.add('active');
}
btn.addEventListener('click', () => {
currentPage = page;
updatePagination();
window.scrollTo({ top: 0, behavior: 'smooth' });
});
return btn;
}
// Search functionality
search.addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
const cards = results.querySelectorAll('.document-card');
cards.forEach(card => {
filteredCards = allCards.filter(card => {
const text = card.textContent.toLowerCase();
card.style.display = text.includes(query) ? 'block' : 'none';
return text.includes(query);
});
currentPage = 1;
updatePagination();
});
// Navigation buttons
prevBtn.addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
updatePagination();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
});
nextBtn.addEventListener('click', () => {
const totalPages = Math.ceil(filteredCards.length / itemsPerPage);
if (currentPage < totalPages) {
currentPage++;
updatePagination();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
});
// Initial render
updatePagination();
</script>

View File

@@ -14,9 +14,9 @@ eleventyComputed:
<a href="/" style="color: #3498db; text-decoration: none;">← Back to home</a>
</div>
<h1>Document {{ doc.document_number }}</h1>
<h1 style="margin-bottom: 1rem;">Document {{ doc.document_number }}</h1>
<div class="metadata">
<div class="metadata-box">
<div class="metadata-grid">
<div class="metadata-item">
<strong>Document Type</strong>
@@ -49,76 +49,113 @@ eleventyComputed:
</div>
</div>
<h2>Full Text</h2>
<div class="full-text">{{ doc.full_text }}</div>
<div class="doc-layout">
<aside class="doc-sidebar">
{% if doc.page_count > 1 %}
<nav class="doc-toc">
<h3>Table of Contents</h3>
<ul class="toc-list">
<li><a href="#full-text">Full Document</a></li>
{% for page in doc.pages %}
<li><a href="#page-{{ loop.index }}">Page {{ page.document_metadata.page_number or loop.index }}</a></li>
{% endfor %}
</ul>
</nav>
{% endif %}
<h2>Referenced Entities</h2>
<div class="entities">
{% if doc.entities.people.length > 0 %}
<div class="entity-group">
<h4>People ({{ doc.entities.people.length }})</h4>
<ul>
{% for person in doc.entities.people %}
<li><a href="/people/#{{ person | slugify }}">{{ person }}</a></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="doc-entities">
<h3>Referenced Entities</h3>
{% if doc.entities.organizations.length > 0 %}
<div class="entity-group">
<h4>Organizations ({{ doc.entities.organizations.length }})</h4>
<ul>
{% for org in doc.entities.organizations %}
<li><a href="/organizations/#{{ org | slugify }}">{{ org }}</a></li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if doc.entities.people.length > 0 %}
<div class="entity-section">
<h4>People ({{ doc.entities.people.length }})</h4>
<ul>
{% for person in doc.entities.people | slice(0, 10) %}
<li><a href="/people/#{{ person | slugify }}">{{ person }}</a></li>
{% endfor %}
{% if doc.entities.people.length > 10 %}
<li style="color: #888; font-style: italic;">+{{ doc.entities.people.length - 10 }} more</li>
{% endif %}
</ul>
</div>
{% endif %}
{% if doc.entities.locations.length > 0 %}
<div class="entity-group">
<h4>Locations ({{ doc.entities.locations.length }})</h4>
<ul>
{% for loc in doc.entities.locations %}
<li><a href="/locations/#{{ loc | slugify }}">{{ loc }}</a></li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if doc.entities.organizations.length > 0 %}
<div class="entity-section">
<h4>Organizations ({{ doc.entities.organizations.length }})</h4>
<ul>
{% for org in doc.entities.organizations | slice(0, 10) %}
<li><a href="/organizations/#{{ org | slugify }}">{{ org }}</a></li>
{% endfor %}
{% if doc.entities.organizations.length > 10 %}
<li style="color: #888; font-style: italic;">+{{ doc.entities.organizations.length - 10 }} more</li>
{% endif %}
</ul>
</div>
{% endif %}
{% if doc.entities.dates.length > 0 %}
<div class="entity-group">
<h4>Dates ({{ doc.entities.dates.length }})</h4>
<ul>
{% for date in doc.entities.dates %}
<li>{{ date }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if doc.entities.locations.length > 0 %}
<div class="entity-section">
<h4>Locations ({{ doc.entities.locations.length }})</h4>
<ul>
{% for loc in doc.entities.locations %}
<li><a href="/locations/#{{ loc | slugify }}">{{ loc }}</a></li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if doc.entities.reference_numbers.length > 0 %}
<div class="entity-group">
<h4>Reference Numbers ({{ doc.entities.reference_numbers.length }})</h4>
<ul>
{% for ref in doc.entities.reference_numbers %}
<li>{{ ref }}</li>
{% if doc.entities.dates.length > 0 %}
<div class="entity-section">
<h4>Dates ({{ doc.entities.dates.length }})</h4>
<ul>
{% for date in doc.entities.dates | slice(0, 5) %}
<li>{{ date }}</li>
{% endfor %}
{% if doc.entities.dates.length > 5 %}
<li style="color: #888; font-style: italic;">+{{ doc.entities.dates.length - 5 }} more</li>
{% endif %}
</ul>
</div>
{% endif %}
{% if doc.entities.reference_numbers.length > 0 %}
<div class="entity-section">
<h4>Reference Numbers</h4>
<ul>
{% for ref in doc.entities.reference_numbers | slice(0, 5) %}
<li>{{ ref }}</li>
{% endfor %}
{% if doc.entities.reference_numbers.length > 5 %}
<li style="color: #888; font-style: italic;">+{{ doc.entities.reference_numbers.length - 5 }} more</li>
{% endif %}
</ul>
</div>
{% endif %}
</div>
</aside>
<main class="doc-main">
<div id="full-text" class="full-text-container">
<h2>Full Text</h2>
<div class="full-text">{{ doc.full_text }}</div>
</div>
{% if doc.page_count > 1 %}
<div class="page-details">
<h2 style="margin-bottom: 1.5rem;">Individual Pages</h2>
{% for page in doc.pages %}
<details class="page-detail" id="page-{{ loop.index }}">
<summary class="page-summary">
Page {{ page.document_metadata.page_number or loop.index }} - {{ page.filename }}
</summary>
<div class="page-content">
<div class="full-text">{{ page.full_text }}</div>
</div>
</details>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
{% endif %}
</main>
</div>
<h2>Page Details</h2>
{% for page in doc.pages %}
<details style="margin-bottom: 1rem;">
<summary style="cursor: pointer; padding: 1rem; background: #f8f9fa; border-radius: 4px;">
Page {{ page.document_metadata.page_number }} - {{ page.filename }}
</summary>
<div style="padding: 1rem; background: white; border: 1px solid #e0e0e0; border-top: none;">
<div class="full-text">{{ page.full_text }}</div>
</div>
</details>
{% endfor %}
</div>

View File

@@ -1,11 +1,17 @@
---
layout: base.njk
title: Document Explorer
title: Home
---
<div class="hero">
<h1>Document Archive</h1>
<h1>Epstein Archive</h1>
<p class="subtitle">Browse {{ documents.length }} processed documents</p>
<div class="search-box" style="margin-top: 2rem; max-width: 600px; margin-left: auto; margin-right: auto;">
<input type="text" id="global-search" placeholder="Search people, organizations, locations...">
</div>
<div id="search-results" style="display: none; margin-top: 1.5rem; text-align: left; max-width: 600px; margin-left: auto; margin-right: auto;"></div>
</div>
<div class="stats">
@@ -82,3 +88,99 @@ title: Document Explorer
</ul>
<a href="/organizations/" class="view-all">View all organizations →</a>
</div>
<script>
const globalSearch = document.getElementById('global-search');
const searchResults = document.getElementById('search-results');
// Build lightweight search index (name and count only)
const searchIndex = {
people: [
{% for person in indices.people %}
{ name: {{ person.name | dump | safe }}, count: {{ person.count }} }{{ "," if not loop.last else "" }}
{% endfor %}
],
organizations: [
{% for org in indices.organizations %}
{ name: {{ org.name | dump | safe }}, count: {{ org.count }} }{{ "," if not loop.last else "" }}
{% endfor %}
],
locations: [
{% for loc in indices.locations %}
{ name: {{ loc.name | dump | safe }}, count: {{ loc.count }} }{{ "," if not loop.last else "" }}
{% endfor %}
]
};
globalSearch.addEventListener('input', (e) => {
const query = e.target.value.toLowerCase().trim();
if (query.length === 0) {
searchResults.style.display = 'none';
return;
}
const results = {
people: searchIndex.people.filter(p => p.name.toLowerCase().includes(query)).slice(0, 5),
organizations: searchIndex.organizations.filter(o => o.name.toLowerCase().includes(query)).slice(0, 5),
locations: searchIndex.locations.filter(l => l.name.toLowerCase().includes(query)).slice(0, 5)
};
const totalResults = results.people.length + results.organizations.length + results.locations.length;
if (totalResults === 0) {
searchResults.innerHTML = '<div style="background: white; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); color: #666;">No results found</div>';
searchResults.style.display = 'block';
return;
}
let html = '<div style="background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">';
if (results.people.length > 0) {
html += '<div style="margin-bottom: 1.5rem;"><h4 style="color: #666; font-size: 0.85rem; text-transform: uppercase; margin-bottom: 0.75rem;">People</h4><div style="display: grid; gap: 0.5rem;">';
results.people.forEach(person => {
html += `<a href="/people/#${slugify(person.name)}" style="display: flex; justify-content: space-between; padding: 0.5rem; background: #f8f9fa; border-radius: 4px; text-decoration: none; color: #3498db; border-left: 3px solid #3498db;">
<span>${person.name}</span>
<span style="color: #999; font-size: 0.85rem;">${person.count} docs</span>
</a>`;
});
html += '</div></div>';
}
if (results.organizations.length > 0) {
html += '<div style="margin-bottom: 1.5rem;"><h4 style="color: #666; font-size: 0.85rem; text-transform: uppercase; margin-bottom: 0.75rem;">Organizations</h4><div style="display: grid; gap: 0.5rem;">';
results.organizations.forEach(org => {
html += `<a href="/organizations/#${slugify(org.name)}" style="display: flex; justify-content: space-between; padding: 0.5rem; background: #f8f9fa; border-radius: 4px; text-decoration: none; color: #3498db; border-left: 3px solid #3498db;">
<span>${org.name}</span>
<span style="color: #999; font-size: 0.85rem;">${org.count} docs</span>
</a>`;
});
html += '</div></div>';
}
if (results.locations.length > 0) {
html += '<div><h4 style="color: #666; font-size: 0.85rem; text-transform: uppercase; margin-bottom: 0.75rem;">Locations</h4><div style="display: grid; gap: 0.5rem;">';
results.locations.forEach(loc => {
html += `<a href="/locations/#${slugify(loc.name)}" style="display: flex; justify-content: space-between; padding: 0.5rem; background: #f8f9fa; border-radius: 4px; text-decoration: none; color: #3498db; border-left: 3px solid #3498db;">
<span>${loc.name}</span>
<span style="color: #999; font-size: 0.85rem;">${loc.count} docs</span>
</a>`;
});
html += '</div></div>';
}
html += '</div>';
searchResults.innerHTML = html;
searchResults.style.display = 'block';
});
// Simple slugify function matching 11ty's slugify filter
function slugify(str) {
return str.toString().toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^\w\-]+/g, '')
.replace(/\-\-+/g, '-')
.replace(/^-+/, '')
.replace(/-+$/, '');
}
</script>

View File

@@ -10,40 +10,65 @@ title: Locations
<input type="text" id="search" placeholder="Search locations...">
</div>
<div class="alphabet-nav">
<button class="letter-btn active" data-letter="all">All</button>
{% for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' %}
<button class="letter-btn" data-letter="{{ letter }}">{{ letter }}</button>
{% endfor %}
</div>
<div id="results">
{% for loc in indices.locations %}
<div class="section" id="{{ loc.name | slugify }}">
<h2>{{ loc.name }}</h2>
<p>Mentioned in {{ loc.count }} documents</p>
<div style="margin-top: 1rem;">
<details class="entity-item" data-name="{{ loc.name }}" data-letter="{{ loc.name[0] | upper }}" id="{{ loc.name | slugify }}">
<summary class="entity-summary">
<span class="entity-name">{{ loc.name }}</span>
<span class="entity-count">{{ loc.count }} {{ "document" if loc.count == 1 else "documents" }}</span>
</summary>
<div class="entity-content">
{% for doc in loc.docs %}
<div class="document-card">
<h4>Document {{ doc.document_number }}</h4>
<div class="meta">
{% if doc.document_metadata.document_type %}Type: {{ doc.document_metadata.document_type }} | {% endif %}
{% if doc.document_metadata.date %}Date: {{ doc.document_metadata.date }} | {% endif %}
Pages: {{ doc.page_count }}
<div class="document-card-compact">
<a href="/document/{{ doc.unique_id | slugify }}/" class="doc-link">
<strong>Document {{ doc.document_number }}</strong>
</a>
<div class="meta-compact">
{% if doc.document_metadata.document_type %}{{ doc.document_metadata.document_type }}{% endif %}
{% if doc.document_metadata.date %} · {{ doc.document_metadata.date }}{% endif %}
· {{ doc.page_count }} {{ "page" if doc.page_count == 1 else "pages" }}
</div>
<a href="/document/{{ doc.unique_id | slugify }}/" style="font-size: 0.9rem;">View document →</a>
</div>
{% endfor %}
</div>
</div>
</details>
{% endfor %}
</div>
<script>
const search = document.getElementById('search');
const results = document.getElementById('results');
const letterBtns = document.querySelectorAll('.letter-btn');
const allItems = results.querySelectorAll('.entity-item');
search.addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
const sections = results.querySelectorAll('.section');
allItems.forEach(item => {
const name = item.dataset.name.toLowerCase();
item.style.display = name.includes(query) ? 'block' : 'none';
});
});
sections.forEach(section => {
const title = section.querySelector('h2').textContent.toLowerCase();
section.style.display = title.includes(query) ? 'block' : 'none';
letterBtns.forEach(btn => {
btn.addEventListener('click', () => {
const letter = btn.dataset.letter;
letterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
search.value = '';
allItems.forEach(item => {
if (letter === 'all') {
item.style.display = 'block';
} else {
item.style.display = item.dataset.letter === letter ? 'block' : 'none';
}
});
});
});
</script>

View File

@@ -10,40 +10,65 @@ title: Organizations
<input type="text" id="search" placeholder="Search organizations...">
</div>
<div class="alphabet-nav">
<button class="letter-btn active" data-letter="all">All</button>
{% for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' %}
<button class="letter-btn" data-letter="{{ letter }}">{{ letter }}</button>
{% endfor %}
</div>
<div id="results">
{% for org in indices.organizations %}
<div class="section" id="{{ org.name | slugify }}">
<h2>{{ org.name }}</h2>
<p>Mentioned in {{ org.count }} documents</p>
<div style="margin-top: 1rem;">
<details class="entity-item" data-name="{{ org.name }}" data-letter="{{ org.name[0] | upper }}" id="{{ org.name | slugify }}">
<summary class="entity-summary">
<span class="entity-name">{{ org.name }}</span>
<span class="entity-count">{{ org.count }} {{ "document" if org.count == 1 else "documents" }}</span>
</summary>
<div class="entity-content">
{% for doc in org.docs %}
<div class="document-card">
<h4>Document {{ doc.document_number }}</h4>
<div class="meta">
{% if doc.document_metadata.document_type %}Type: {{ doc.document_metadata.document_type }} | {% endif %}
{% if doc.document_metadata.date %}Date: {{ doc.document_metadata.date }} | {% endif %}
Pages: {{ doc.page_count }}
<div class="document-card-compact">
<a href="/document/{{ doc.unique_id | slugify }}/" class="doc-link">
<strong>Document {{ doc.document_number }}</strong>
</a>
<div class="meta-compact">
{% if doc.document_metadata.document_type %}{{ doc.document_metadata.document_type }}{% endif %}
{% if doc.document_metadata.date %} · {{ doc.document_metadata.date }}{% endif %}
· {{ doc.page_count }} {{ "page" if doc.page_count == 1 else "pages" }}
</div>
<a href="/document/{{ doc.unique_id | slugify }}/" style="font-size: 0.9rem;">View document →</a>
</div>
{% endfor %}
</div>
</div>
</details>
{% endfor %}
</div>
<script>
const search = document.getElementById('search');
const results = document.getElementById('results');
const letterBtns = document.querySelectorAll('.letter-btn');
const allItems = results.querySelectorAll('.entity-item');
search.addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
const sections = results.querySelectorAll('.section');
allItems.forEach(item => {
const name = item.dataset.name.toLowerCase();
item.style.display = name.includes(query) ? 'block' : 'none';
});
});
sections.forEach(section => {
const title = section.querySelector('h2').textContent.toLowerCase();
section.style.display = title.includes(query) ? 'block' : 'none';
letterBtns.forEach(btn => {
btn.addEventListener('click', () => {
const letter = btn.dataset.letter;
letterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
search.value = '';
allItems.forEach(item => {
if (letter === 'all') {
item.style.display = 'block';
} else {
item.style.display = item.dataset.letter === letter ? 'block' : 'none';
}
});
});
});
</script>

View File

@@ -10,40 +10,73 @@ title: People
<input type="text" id="search" placeholder="Search people...">
</div>
<div class="alphabet-nav">
<button class="letter-btn active" data-letter="all">All</button>
{% for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' %}
<button class="letter-btn" data-letter="{{ letter }}">{{ letter }}</button>
{% endfor %}
</div>
<div id="results">
{% for person in indices.people %}
<div class="section" id="{{ person.name | slugify }}">
<h2>{{ person.name }}</h2>
<p>Mentioned in {{ person.count }} documents</p>
<div style="margin-top: 1rem;">
<details class="entity-item" data-name="{{ person.name }}" data-letter="{{ person.name[0] | upper }}" id="{{ person.name | slugify }}">
<summary class="entity-summary">
<span class="entity-name">{{ person.name }}</span>
<span class="entity-count">{{ person.count }} {{ "document" if person.count == 1 else "documents" }}</span>
</summary>
<div class="entity-content">
{% for doc in person.docs %}
<div class="document-card">
<h4>Document {{ doc.document_number }}</h4>
<div class="meta">
{% if doc.document_metadata.document_type %}Type: {{ doc.document_metadata.document_type }} | {% endif %}
{% if doc.document_metadata.date %}Date: {{ doc.document_metadata.date }} | {% endif %}
Pages: {{ doc.page_count }}
<div class="document-card-compact">
<a href="/document/{{ doc.unique_id | slugify }}/" class="doc-link">
<strong>Document {{ doc.document_number }}</strong>
</a>
<div class="meta-compact">
{% if doc.document_metadata.document_type %}{{ doc.document_metadata.document_type }}{% endif %}
{% if doc.document_metadata.date %} · {{ doc.document_metadata.date }}{% endif %}
· {{ doc.page_count }} {{ "page" if doc.page_count == 1 else "pages" }}
</div>
<a href="/document/{{ doc.unique_id | slugify }}/" style="font-size: 0.9rem;">View document →</a>
</div>
{% endfor %}
</div>
</div>
</details>
{% endfor %}
</div>
<script>
const search = document.getElementById('search');
const results = document.getElementById('results');
const letterBtns = document.querySelectorAll('.letter-btn');
const allItems = results.querySelectorAll('.entity-item');
// Search functionality
search.addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
const sections = results.querySelectorAll('.section');
allItems.forEach(item => {
const name = item.dataset.name.toLowerCase();
item.style.display = name.includes(query) ? 'block' : 'none';
});
});
sections.forEach(section => {
const title = section.querySelector('h2').textContent.toLowerCase();
section.style.display = title.includes(query) ? 'block' : 'none';
// Alphabet filter
letterBtns.forEach(btn => {
btn.addEventListener('click', () => {
const letter = btn.dataset.letter;
// Update active state
letterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// Clear search
search.value = '';
// Filter items
allItems.forEach(item => {
if (letter === 'all') {
item.style.display = 'block';
} else {
item.style.display = item.dataset.letter === letter ? 'block' : 'none';
}
});
});
});
</script>