overview / js /pages /AreaPage.js
Yacine Jernite
v07
8ed13f7
raw
history blame
9.04 kB
// pages/AreaPage.js - Single-topic view for area pages
import { getFeaturedArtifacts } from '../init.js';
import { createArtifactCarousel } from '../cards/ArtifactSummaryCard.js';
import { renderAreaNavigation } from '../components/PageNavigation.js';
import { renderContentSection, renderOpennessCallout } from '../components/ContentSection.js';
// Use global areasData (loaded in index.html <head>)
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: () => {}
};
}
// If no topic specified, show overview of the area
if (!topicId) {
return renderAreaOverview(area, areaId);
}
// Show specific topic
const topic = area.topics[topicId];
if (!topic) {
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">Topic not found</h1></div>`,
init: () => {}
};
}
return renderTopicView(area, areaId, topic, topicId);
}
function renderAreaOverview(area, areaId) {
// Get topics as array
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, null)}
${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-700 leading-relaxed mb-4">${p}</p>`).join('')
: `<p class="text-gray-700 leading-relaxed">${area.description.short || area.description}</p>`
}
</div>
<!-- Topics within this area -->
<div class="mb-8">
<p class="text-xs 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
let bgColor = 'bg-gray-200';
let textColor = 'text-gray-700';
if (topic.color) {
const bgMatch = topic.color.match(/bg-(\w+)-(\d+)/);
const textMatch = topic.color.match(/text-(\w+)-(\d+)/);
if (bgMatch) bgColor = `bg-${bgMatch[1]}-${bgMatch[2]}`;
if (textMatch) textColor = `text-${textMatch[1]}-700`;
}
// 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 ${bgColor} ${textColor} rounded hover:opacity-80 transition-opacity"${titleAttr}>
${topic.navName}
</a>
`;
}).join('')}
</div>
</div>
<!-- Research and Resources -->
<div>
<p class="text-xs 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>
`)}
`;
return {
content,
init: () => {
initializeAreaArtifactsCarousel(areaId);
}
};
}
function renderTopicView(area, areaId, topic, topicId) {
const topicName = topic.navName || topic.name;
const topicDescription = topic.description?.paragraphs
? topic.description.paragraphs.map(p => `<p class="text-gray-700 leading-relaxed mb-4">${p}</p>`).join('')
: (topic.description?.short || topic.description || '');
// Get all topics for navigation
const allTopics = Object.entries(area.topics).map(([key, value]) => ({
id: key,
navName: typeof value === 'string' ? value : (value.navName || value.name)
}));
const content = `
<!-- Top spacing -->
<div class="mb-8"></div>
${renderAreaNavigation(areaId, area.navTitle, allTopics, topicId)}
${renderContentSection(topicId, `
<!-- Breadcrumb -->
<div class="mb-4 text-sm text-gray-600">
<a href="/${areaId}" 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>
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">${topic.name}</h1>
<!-- Topic Description -->
${topicDescription ? `
<div class="mb-8">
${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>
`)}
`;
return {
content,
init: () => {
// Initialize artifact carousel for this topic
setTimeout(async () => {
try {
const allArtifacts = window.allArtifacts || [];
// Filter artifacts by this specific topic
const filteredArtifacts = allArtifacts.filter(artifact =>
artifact.topics && artifact.topics.includes(topicId)
);
// Transform field names to match expected format
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);
}
};
}
// Helper function to initialize area artifacts carousel
function initializeAreaArtifactsCarousel(areaId) {
// Wait for DOM to be ready
setTimeout(() => {
try {
const allArtifacts = window.allArtifacts || [];
// Filter artifacts by area
const filteredArtifacts = allArtifacts.filter(artifact =>
artifact.areas && artifact.areas.includes(areaId)
);
// Transform field names to match what the carousel expects
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);
}