|
|
|
|
|
import { getFeaturedArtifacts } from '../init.js'; |
|
|
import { createArtifactCarousel } from '../components/Carousel.js'; |
|
|
import { renderAreaNavigation } from '../components/PageNavigation.js'; |
|
|
import { renderContentSection, renderOpennessCallout } from '../components/ContentSection.js'; |
|
|
import { initializeStickyNavigation } from '../utils/stickyNavigation.js'; |
|
|
|
|
|
|
|
|
const areasData = window.areasData; |
|
|
|
|
|
export function renderAreaPage(areaId, topicId = null) { |
|
|
const area = areasData[areaId]; |
|
|
if (!area) { |
|
|
return { |
|
|
content: `<div class="bg-white/90 backdrop-blur-md rounded-lg shadow-sm p-8 mx-4"><h1 class="text-2xl font-bold text-red-600">Area not found</h1></div>`, |
|
|
init: () => {} |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
return renderFullAreaPage(area, areaId, topicId); |
|
|
} |
|
|
|
|
|
function renderFullAreaPage(area, areaId, initialTopicId = null) { |
|
|
|
|
|
const topics = Object.entries(area.topics).map(([key, value]) => ({ |
|
|
id: key, |
|
|
name: typeof value === 'string' ? value : value.name, |
|
|
navName: typeof value === 'string' ? value : (value.navName || value.name), |
|
|
description: typeof value === 'string' ? '' : (value.description || ''), |
|
|
color: typeof value === 'object' ? value.color : 'bg-gray-200 text-gray-700' |
|
|
})); |
|
|
|
|
|
const content = ` |
|
|
<!-- Top spacing --> |
|
|
<div class="mb-8"></div> |
|
|
|
|
|
${renderAreaNavigation(areaId, area.navTitle, topics, initialTopicId)} |
|
|
|
|
|
<!-- Overview Section --> |
|
|
${renderContentSection('overview', ` |
|
|
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">${area.title}</h1> |
|
|
|
|
|
<!-- Area Description --> |
|
|
<div class="mb-8"> |
|
|
${area.description.paragraphs ? |
|
|
area.description.paragraphs.map(p => `<p class="text-gray-900 leading-relaxed mb-4">${p}</p>`).join('') |
|
|
: `<p class="text-gray-900 leading-relaxed">${area.description.short || area.description}</p>` |
|
|
} |
|
|
</div> |
|
|
|
|
|
<!-- Topics within this area --> |
|
|
<div class="mb-8"> |
|
|
<p class="text-sm font-semibold text-gray-600 uppercase tracking-wide mb-3">Topics in this area:</p> |
|
|
<div class="flex flex-wrap gap-2"> |
|
|
${topics.map(topic => { |
|
|
// Extract colors from topic.color and enhance them |
|
|
let bgColor = 'bg-gray-200'; |
|
|
let textColor = 'text-gray-800'; |
|
|
let borderColor = 'border-gray-300'; |
|
|
|
|
|
if (topic.color) { |
|
|
const bgMatch = topic.color.match(/bg-(\w+)-(\d+)/); |
|
|
const textMatch = topic.color.match(/text-(\w+)-(\d+)/); |
|
|
if (bgMatch) { |
|
|
const colorName = bgMatch[1]; |
|
|
const shade = parseInt(bgMatch[2]); |
|
|
// Increase saturation: 100 β 200, 200 β 300, etc. |
|
|
const newShade = Math.min(shade + 100, 300); |
|
|
bgColor = `bg-${colorName}-${newShade}`; |
|
|
borderColor = `border-${colorName}-${Math.min(newShade + 100, 400)}`; |
|
|
} |
|
|
if (textMatch) { |
|
|
const colorName = textMatch[1]; |
|
|
textColor = `text-${colorName}-900`; |
|
|
} |
|
|
} |
|
|
|
|
|
// Get short description for tooltip |
|
|
const shortDesc = topic.description?.short || (typeof topic.description === 'string' ? topic.description.split('.')[0] + '.' : ''); |
|
|
const titleAttr = shortDesc ? ` title="${shortDesc}"` : ''; |
|
|
|
|
|
return ` |
|
|
<a href="/${areaId}#${topic.id}" class="inline-block px-3 py-1.5 text-sm font-semibold ${bgColor} ${textColor} ${borderColor} border rounded hover:opacity-90 transition-opacity"${titleAttr}> |
|
|
${topic.navName} |
|
|
</a> |
|
|
`; |
|
|
}).join('')} |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<!-- Research and Resources --> |
|
|
<div> |
|
|
<p class="text-sm font-semibold text-gray-600 uppercase tracking-wide mb-3">Research and Resources:</p> |
|
|
<div id="area-artifacts-carousel-${areaId}" class="overflow-x-auto -mx-2"> |
|
|
<!-- Carousel will be inserted here --> |
|
|
</div> |
|
|
</div> |
|
|
`)} |
|
|
|
|
|
<!-- Topic Sections (each as a separate content section) --> |
|
|
${topics.map(topic => renderTopicSection(area, areaId, topic)).join('')} |
|
|
`; |
|
|
|
|
|
return { |
|
|
content, |
|
|
init: () => { |
|
|
initializeAreaArtifactsCarousel(areaId); |
|
|
|
|
|
topics.forEach(topic => { |
|
|
initializeTopicArtifactsCarousel(areaId, topic.id); |
|
|
}); |
|
|
initializeStickyNavigation('nav-container'); |
|
|
} |
|
|
}; |
|
|
} |
|
|
|
|
|
function renderTopicSection(area, areaId, topic) { |
|
|
const topicId = topic.id; |
|
|
const topicName = topic.navName || topic.name; |
|
|
const topicDescription = topic.description?.paragraphs |
|
|
? topic.description.paragraphs.map(p => `<p class="text-gray-900 leading-relaxed mb-4">${p}</p>`).join('') |
|
|
: (topic.description?.short || topic.description || ''); |
|
|
|
|
|
return renderContentSection(topicId, ` |
|
|
<!-- Breadcrumb --> |
|
|
<div class="mb-4 text-sm text-gray-600"> |
|
|
<a href="/${areaId}#overview" class="hover:text-blue-600 transition-colors">${area.navTitle}</a> |
|
|
<span class="mx-2">βΊ</span> |
|
|
<span class="text-gray-900 font-medium">${topicName}</span> |
|
|
</div> |
|
|
|
|
|
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-6">${topic.name}</h2> |
|
|
|
|
|
<!-- Topic Description --> |
|
|
${topicDescription ? ` |
|
|
<div class="mb-8 text-gray-900"> |
|
|
${topicDescription} |
|
|
</div> |
|
|
` : ''} |
|
|
|
|
|
<h3 class="text-xl md:text-2xl font-bold text-gray-900 mb-6">Related Research & Resources</h3> |
|
|
<div id="${topicId}-artifacts-carousel" class="overflow-x-auto -mx-2"> |
|
|
<!-- Carousel will be inserted here --> |
|
|
</div> |
|
|
`); |
|
|
} |
|
|
|
|
|
|
|
|
function initializeAreaArtifactsCarousel(areaId) { |
|
|
|
|
|
setTimeout(() => { |
|
|
try { |
|
|
const allArtifacts = window.allArtifacts || []; |
|
|
|
|
|
|
|
|
const filteredArtifacts = allArtifacts.filter(artifact => |
|
|
artifact.areas && artifact.areas.includes(areaId) |
|
|
); |
|
|
|
|
|
|
|
|
const transformedArtifacts = filteredArtifacts.map(artifact => ({ |
|
|
...artifact, |
|
|
areaTags: artifact.areas, |
|
|
subAreaTags: artifact.topics, |
|
|
sourceUrl: artifact.url |
|
|
})); |
|
|
|
|
|
const carouselId = `area-artifacts-carousel-${areaId}`; |
|
|
createArtifactCarousel(transformedArtifacts.length > 0 ? transformedArtifacts : getFeaturedArtifacts(), carouselId); |
|
|
} catch (error) { |
|
|
console.error(`Error creating carousel for area ${areaId}:`, error); |
|
|
const carouselId = `area-artifacts-carousel-${areaId}`; |
|
|
createArtifactCarousel(getFeaturedArtifacts(), carouselId); |
|
|
} |
|
|
}, 50); |
|
|
} |
|
|
|
|
|
|
|
|
function initializeTopicArtifactsCarousel(areaId, topicId) { |
|
|
setTimeout(() => { |
|
|
try { |
|
|
const allArtifacts = window.allArtifacts || []; |
|
|
|
|
|
|
|
|
const filteredArtifacts = allArtifacts.filter(artifact => |
|
|
artifact.topics && artifact.topics.includes(topicId) |
|
|
); |
|
|
|
|
|
|
|
|
const transformedArtifacts = filteredArtifacts.map(artifact => ({ |
|
|
...artifact, |
|
|
areaTags: artifact.areas, |
|
|
subAreaTags: artifact.topics, |
|
|
sourceUrl: artifact.url |
|
|
})); |
|
|
|
|
|
const carouselId = `${topicId}-artifacts-carousel`; |
|
|
createArtifactCarousel(transformedArtifacts.length > 0 ? transformedArtifacts : getFeaturedArtifacts(), carouselId); |
|
|
} catch (error) { |
|
|
console.error(`Error creating carousel for ${topicId}:`, error); |
|
|
const carouselId = `${topicId}-artifacts-carousel`; |
|
|
createArtifactCarousel(getFeaturedArtifacts(), carouselId); |
|
|
} |
|
|
}, 50); |
|
|
} |
|
|
|