feat(ui): integrate value pluralism documents in docs viewer

- Update docs.html with MongoDB-integrated documents
- Add value pluralism documents to sidebar categories
- Update docs-app.js for proper document retrieval
- Sync navbar changes across UI pages

Documents now searchable and properly categorized in docs viewer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
TheFlow 2025-10-12 16:36:42 +13:00
parent c0f4b27889
commit ea3264d4da
3 changed files with 118 additions and 134 deletions

View file

@ -442,7 +442,7 @@
<option value="">All Audiences</option>
<option value="researcher">Researcher</option>
<option value="implementer">Implementer</option>
<option value="advocate">Advocate / Leader</option>
<option value="leader">Leader</option>
<option value="technical">Technical</option>
<option value="general">General</option>
</select>
@ -662,7 +662,7 @@
</li>
<li class="flex items-start">
<span class="font-medium text-blue-600 mr-2"></span>
<span><strong>Audience:</strong> Filter for Researcher, Implementer, Advocate/Leader, Technical, or General audience</span>
<span><strong>Audience:</strong> Filter for Researcher, Implementer, Leader, Technical, or General audience</span>
</li>
</ul>
</div>

View file

@ -6,7 +6,6 @@
class TractatusNavbar {
constructor() {
this.mobileMenuOpen = false;
this.audiencesDropdownOpen = false;
this.init();
}
@ -30,32 +29,9 @@ class TractatusNavbar {
</a>
</div>
<!-- Desktop Menu (hidden on mobile) -->
<div class="hidden md:flex items-center space-x-8">
<!-- Audiences Dropdown -->
<div class="relative">
<button id="audiences-dropdown-btn" class="text-gray-600 hover:text-gray-900 flex items-center space-x-1 focus:outline-none focus:ring-2 focus:ring-blue-500 rounded px-2 py-1">
<span>Audiences</span>
<svg class="w-4 h-4 transition-transform" id="audiences-dropdown-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<div id="audiences-dropdown-menu" class="hidden absolute left-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 py-2">
<a href="/researcher.html" class="block px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-700">Researcher</a>
<a href="/implementer.html" class="block px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-700">Implementer</a>
<a href="/leader.html" class="block px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-700">Leader</a>
</div>
</div>
<a href="/docs.html" class="text-gray-600 hover:text-gray-900 font-medium">Docs</a>
<a href="/blog.html" class="text-gray-600 hover:text-gray-900 font-medium">Blog</a>
<a href="/faq.html" class="text-gray-600 hover:text-gray-900 font-medium">FAQ</a>
<a href="/about.html" class="text-gray-600 hover:text-gray-900">About</a>
</div>
<!-- Menu Button (always visible) -->
<!-- Right: Menu Button (always visible) -->
<div class="flex items-center">
<button id="mobile-menu-btn" class="text-gray-600 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 rounded p-2 md:ml-4" aria-label="Toggle menu">
<button id="mobile-menu-btn" class="text-gray-600 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 rounded p-2" aria-label="Toggle menu">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
@ -99,6 +75,9 @@ class TractatusNavbar {
<a href="/docs.html" class="block px-3 py-2.5 text-gray-700 hover:bg-blue-50 hover:text-blue-700 rounded-lg transition">
<span class="text-sm font-semibold">📚 Documentation</span>
</a>
<a href="/api-reference.html" class="block px-3 py-2.5 text-gray-700 hover:bg-blue-50 hover:text-blue-700 rounded-lg transition">
<span class="text-sm font-semibold">🔌 API Reference</span>
</a>
<a href="/blog.html" class="block px-3 py-2.5 text-gray-700 hover:bg-blue-50 hover:text-blue-700 rounded-lg transition">
<span class="text-sm font-semibold">📝 Blog</span>
</a>
@ -128,29 +107,6 @@ class TractatusNavbar {
}
attachEventListeners() {
// Audiences Dropdown (Desktop)
const audiencesBtn = document.getElementById('audiences-dropdown-btn');
const audiencesMenu = document.getElementById('audiences-dropdown-menu');
const audiencesArrow = document.getElementById('audiences-dropdown-arrow');
if (audiencesBtn) {
audiencesBtn.addEventListener('click', (e) => {
e.stopPropagation();
this.audiencesDropdownOpen = !this.audiencesDropdownOpen;
audiencesMenu.classList.toggle('hidden', !this.audiencesDropdownOpen);
audiencesArrow.style.transform = this.audiencesDropdownOpen ? 'rotate(180deg)' : 'rotate(0deg)';
});
// Close dropdown when clicking outside
document.addEventListener('click', () => {
if (this.audiencesDropdownOpen) {
this.audiencesDropdownOpen = false;
audiencesMenu.classList.add('hidden');
audiencesArrow.style.transform = 'rotate(0deg)';
}
});
}
// Mobile Menu (Navigation Drawer)
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
const mobileMenuCloseBtn = document.getElementById('mobile-menu-close-btn');

View file

@ -7,12 +7,12 @@ if (typeof DocumentCards !== 'undefined') {
documentCards = new DocumentCards('document-content');
}
// Document categorization - Organized by audience and expertise level
// Document categorization - Granular categories for better organization
const CATEGORIES = {
'introduction': {
label: '📘 Introduction',
icon: '📘',
description: 'Start here - core concepts for all audiences',
'getting-started': {
label: '🚀 Getting Started',
icon: '🚀',
description: 'Introduction, core concepts, and quick start guides',
order: 1,
color: 'blue',
bgColor: 'bg-blue-50',
@ -20,10 +20,10 @@ const CATEGORIES = {
textColor: 'text-blue-700',
collapsed: false
},
'implementation': {
label: '⚙️ Implementation',
icon: '⚙️',
description: 'Practical guides for developers and implementers',
'technical-reference': {
label: '🔌 Technical Reference',
icon: '🔌',
description: 'API docs, implementation guides, code examples',
order: 2,
color: 'green',
bgColor: 'bg-green-50',
@ -31,10 +31,10 @@ const CATEGORIES = {
textColor: 'text-green-700',
collapsed: false
},
'case-studies': {
label: '📊 Case Studies',
icon: '📊',
description: 'Real-world examples and failure analysis',
'research-theory': {
label: '🔬 Research & Theory',
icon: '🔬',
description: 'Research papers, theoretical foundations',
order: 3,
color: 'purple',
bgColor: 'bg-purple-50',
@ -42,27 +42,49 @@ const CATEGORIES = {
textColor: 'text-purple-700',
collapsed: false
},
'business': {
label: '💼 Business Strategy',
icon: '💼',
description: 'ROI, business case, and strategic planning',
'case-studies': {
label: '📊 Case Studies',
icon: '📊',
description: 'Real-world examples, failure modes, success stories',
order: 4,
color: 'amber',
bgColor: 'bg-amber-50',
borderColor: 'border-l-4 border-amber-500',
textColor: 'text-amber-700',
collapsed: false
},
'deployment-operations': {
label: '⚙️ Deployment & Operations',
icon: '⚙️',
description: 'Deployment guides, architecture, troubleshooting',
order: 5,
color: 'indigo',
bgColor: 'bg-indigo-50',
borderColor: 'border-l-4 border-indigo-500',
textColor: 'text-indigo-700',
collapsed: false
collapsed: true
},
'advanced': {
label: '🔬 Advanced Topics',
icon: '🔬',
description: 'Deep technical details and research',
order: 5,
color: 'red',
bgColor: 'bg-red-50',
borderColor: 'border-l-4 border-red-500',
textColor: 'text-red-700',
collapsed: true // Collapsed by default
'business-leadership': {
label: '💼 Business & Leadership',
icon: '💼',
description: 'Business cases, executive briefs, ROI analysis',
order: 6,
color: 'pink',
bgColor: 'bg-pink-50',
borderColor: 'border-l-4 border-pink-500',
textColor: 'text-pink-700',
collapsed: true
},
'downloads-resources': {
label: '📥 Downloads & Resources',
icon: '📥',
description: 'PDFs, sample configs, external resources',
order: 7,
color: 'gray',
bgColor: 'bg-gray-50',
borderColor: 'border-l-4 border-gray-400',
textColor: 'text-gray-700',
collapsed: true
}
};
@ -75,8 +97,8 @@ const HIDDEN_DOCS = [
'cover-letter'
];
// Categorize a document based on order field
// Order ranges map to categories for audience/expertise-based organization
// Categorize a document using database category field
// New granular category system for better document organization
function categorizeDocument(doc) {
const slug = doc.slug.toLowerCase();
@ -85,35 +107,17 @@ function categorizeDocument(doc) {
return null;
}
const order = doc.order || 999;
// Use category from database
const category = doc.category || 'downloads-resources';
// Introduction: 1-5 (beginner level, all audiences)
if (order >= 1 && order <= 5) {
return 'introduction';
// Validate category exists in CATEGORIES constant
if (CATEGORIES[category]) {
return category;
}
// Implementation: 10-19 (practical/technical for implementers)
if (order >= 10 && order <= 19) {
return 'implementation';
}
// Case Studies: 20-29 (real-world examples)
if (order >= 20 && order <= 29) {
return 'case-studies';
}
// Business Strategy: 30-35 (for leaders/decision makers)
if (order >= 30 && order <= 35) {
return 'business';
}
// Advanced Topics: 40-49 (deep technical/research)
if (order >= 40 && order <= 49) {
return 'advanced';
}
// Fallback to introduction for uncategorized (order 999)
return 'introduction';
// Fallback to downloads-resources for uncategorized
console.warn(`Document "${doc.title}" has invalid category "${category}", using fallback`);
return 'downloads-resources';
}
// Group documents by category
@ -140,21 +144,36 @@ function groupDocuments(docs) {
function renderDocLink(doc, isHighlighted = false) {
const highlightClass = isHighlighted ? 'text-blue-700 bg-blue-50 border border-blue-200' : '';
// Determine if PDF download is available
// Check if metadata indicates PDF existence (presence in slugs that typically have PDFs)
// Documents with download_formats.pdf, markdown files, or most docs should have PDFs
// API Reference docs and technical diagrams might not have PDFs
const hasPDF = !doc.slug.includes('api-reference-complete') &&
!doc.slug.includes('openapi-specification') &&
!doc.slug.includes('api-javascript-examples') &&
!doc.slug.includes('api-python-examples') &&
!doc.slug.includes('technical-architecture-diagram');
// Add download button styling
const paddingClass = hasPDF ? 'pr-10' : 'pr-3';
return `
<div class="relative mb-1">
<button class="doc-link w-full text-left px-3 py-2 pr-10 rounded text-sm hover:bg-blue-50 transition ${highlightClass}"
<button class="doc-link w-full text-left px-3 py-2 ${paddingClass} rounded text-sm hover:bg-blue-50 transition ${highlightClass}"
data-slug="${doc.slug}">
<div class="font-medium text-gray-900">${doc.title}</div>
</button>
<a href="/downloads/${doc.slug}.pdf"
target="_blank"
rel="noopener noreferrer"
class="doc-download-link"
title="Download PDF (opens in new tab)">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
</a>
${hasPDF ? `
<a href="/downloads/${doc.slug}.pdf"
target="_blank"
rel="noopener noreferrer"
class="doc-download-link"
title="Download PDF (opens in new tab)">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
</a>
` : ''}
</div>
`;
}
@ -211,8 +230,8 @@ async function loadDocuments() {
// Render documents in category
docs.forEach(doc => {
// Highlight the first document in Introduction category
const isHighlighted = categoryId === 'introduction' && doc.order === 1;
// Highlight the first document in Getting Started category
const isHighlighted = categoryId === 'getting-started' && doc.order === 1;
html += renderDocLink(doc, isHighlighted);
});
@ -290,15 +309,15 @@ async function loadDocuments() {
}
});
// Auto-load first document in "Introduction" category (order: 1)
const introductionDocs = grouped['introduction'] || [];
if (introductionDocs.length > 0) {
// Auto-load first document in "Getting Started" category (order: 1)
const gettingStartedDocs = grouped['getting-started'] || [];
if (gettingStartedDocs.length > 0) {
// Load the first document (order: 1) if available
const firstDoc = introductionDocs.find(d => d.order === 1);
const firstDoc = gettingStartedDocs.find(d => d.order === 1);
if (firstDoc) {
loadDocument(firstDoc.slug);
} else {
loadDocument(introductionDocs[0].slug);
loadDocument(gettingStartedDocs[0].slug);
}
} else if (documents.length > 0) {
// Fallback to first available document in any category
@ -361,6 +380,13 @@ async function loadDocument(slug) {
// Fallback to traditional view with header
const hasToC = currentDocument.toc && currentDocument.toc.length > 0;
// Check if PDF is available (same logic as sidebar)
const hasPDF = !currentDocument.slug.includes('api-reference-complete') &&
!currentDocument.slug.includes('openapi-specification') &&
!currentDocument.slug.includes('api-javascript-examples') &&
!currentDocument.slug.includes('api-python-examples') &&
!currentDocument.slug.includes('technical-architecture-diagram');
let headerHTML = `
<div class="flex items-center justify-between mb-6 pb-4 border-b border-gray-200">
<h1 class="text-3xl font-bold text-gray-900">${currentDocument.title}</h1>
@ -375,16 +401,18 @@ async function loadDocument(slug) {
</svg>
</button>
` : ''}
<a href="/downloads/${currentDocument.slug}.pdf"
target="_blank"
rel="noopener noreferrer"
class="p-2 text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded transition"
title="Download PDF"
aria-label="Download PDF">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
</a>
${hasPDF ? `
<a href="/downloads/${currentDocument.slug}.pdf"
target="_blank"
rel="noopener noreferrer"
class="p-2 text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded transition"
title="Download PDF"
aria-label="Download PDF">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
</a>
` : ''}
</div>
</div>
`;