overview / js /pages /AreaPage.js
yjernite's picture
yjernite HF Staff
Upload 3 files
cddcbaf verified
raw
history blame
10.5 kB
// pages/AreaPage.js - Single-topic view for area pages
import { areasData } from '../data/areas.js';
import { featuredArtifacts } from '../data/artifacts.js';
import { createArtifactCarousel } from '../cards/ArtifactSummaryCard.js';
import { sampleResources } from '../data/resources.js';
import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js';
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.subAreas[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 sub-areas as array
const topics = Object.entries(area.subAreas).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>
<!-- Page Navigation Card -->
<section class="mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
<div class="bg-white/70 backdrop-blur-sm shadow-sm rounded-lg px-6 py-4 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
<div class="flex items-center gap-3 overflow-x-auto">
<span class="text-gray-600 font-semibold whitespace-nowrap">${area.navTitle}:</span>
<a href="/${areaId}" class="px-3 py-1.5 text-sm font-semibold text-blue-600 underline decoration-2 underline-offset-2 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Overview</a>
${topics.map((topic, index) => `
${index === 0 ? '<span class="text-gray-300">β€’</span>' : ''}
<a href="/${areaId}/${topic.id}" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">${topic.navName}</a>
${index < topics.length - 1 ? '<span class="text-gray-300">β€’</span>' : ''}
`).join('')}
</div>
</div>
</section>
<!-- Area Overview Section -->
<section id="overview" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
<div class="bg-white/90 backdrop-blur-md shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">${area.title}</h1>
<!-- Area Description -->
<div class="mb-8">
<p class="text-gray-700 leading-relaxed">${area.description}</p>
</div>
<!-- Role of Openness -->
${area.openness ? `
<div class="px-4 sm:px-6 py-5 bg-gradient-to-r from-orange-50/95 to-yellow-50/95 backdrop-blur-sm border-l-4 border-orange-400 rounded-r-lg mb-8">
<p class="font-bold text-orange-900 mb-3 text-lg">πŸ€— The Role of Openness</p>
<p class="text-orange-800 italic leading-relaxed">${area.openness}</p>
</div>
` : ''}
<!-- Topics within this area -->
<div>
<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`;
}
return `
<a href="/${areaId}/${topic.id}" class="inline-block px-3 py-1.5 text-sm ${bgColor} ${textColor} rounded hover:opacity-80 transition-opacity">
${topic.navName}
</a>
`;
}).join('')}
</div>
</div>
</div>
</section>
`;
return {
content,
init: () => {}
};
}
function renderTopicView(area, areaId, topic, topicId) {
const topicName = topic.navName || topic.name;
const topicDescription = topic.description || '';
const topicOpenness = topic.openness || '';
// Get all topics for navigation
const allTopics = Object.entries(area.subAreas).map(([key, value]) => ({
id: key,
navName: typeof value === 'string' ? value : (value.navName || value.name)
}));
const content = `
<!-- Top spacing -->
<div class="mb-8"></div>
<!-- Page Navigation Card -->
<section class="mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
<div class="bg-white/70 backdrop-blur-sm shadow-sm rounded-lg px-6 py-4 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
<div class="flex items-center gap-3 overflow-x-auto">
<span class="text-gray-600 font-semibold whitespace-nowrap">${area.navTitle}:</span>
<a href="/${areaId}" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Overview</a>
${allTopics.map((t, index) => `
${index === 0 ? '<span class="text-gray-300">β€’</span>' : ''}
<a href="/${areaId}/${t.id}" class="px-3 py-1.5 text-sm font-medium transition-colors whitespace-nowrap rounded ${t.id === topicId ? 'text-blue-600 font-semibold underline decoration-2 underline-offset-2' : 'text-gray-700 hover:text-blue-600 hover:bg-blue-50'}">${t.navName}</a>
${index < allTopics.length - 1 ? '<span class="text-gray-300">β€’</span>' : ''}
`).join('')}
</div>
</div>
</section>
<!-- Topic Content Section -->
<section id="${topicId}" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
<div class="bg-white/80 backdrop-blur-md shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
<!-- 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">
<p class="text-gray-700 leading-relaxed">${topicDescription}</p>
</div>
` : ''}
<!-- Role of Openness for this topic -->
${topicOpenness ? `
<div class="px-4 sm:px-6 py-5 bg-gradient-to-r from-orange-50/95 to-yellow-50/95 backdrop-blur-sm border-l-4 border-orange-400 rounded-r-lg mb-8">
<p class="font-bold text-orange-900 mb-3 text-lg">πŸ€— The Role of Openness</p>
<p class="text-orange-800 italic leading-relaxed">${topicOpenness}</p>
</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>
</div>
</section>
`;
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 : featuredArtifacts, carouselId);
} catch (error) {
console.error(`Error creating carousel for ${topicId}:`, error);
const carouselId = `${topicId}-artifacts-carousel`;
createArtifactCarousel(featuredArtifacts, carouselId);
}
}, 50);
}
};
}