overview / js /pages /AreaPage.js
yjernite's picture
yjernite HF Staff
Upload 3 files
930894d verified
raw
history blame
12.2 kB
// pages/AreaPage.js - Reusable area page component
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) {
const area = areasData[areaId];
if (!area) {
return {
content: `<div class="bg-white rounded-lg shadow-sm p-8"><h1 class="text-2xl font-bold text-red-600">Area not found</h1></div>`,
init: () => {}
};
}
// Filter resources by area
const areaResources = sampleResources.filter(resource =>
resource.areaTags.includes(areaId)
);
// Get sub-areas as array for easier processing
const subAreas = Object.entries(area.subAreas).map(([key, value]) => ({
id: key,
name: typeof value === 'string' ? value : value.name,
description: typeof value === 'string' ? '' : (value.description || ''),
openness: typeof value === 'string' ? '' : (value.openness || ''),
gradient: typeof value === 'object' ? value.gradient : null
}));
const content = `
<!-- Page Background Image for Main Content -->
<div class="fixed opacity-40 z-0" style="top: 104px; left: 256px; right: 0; bottom: 0;" id="area-background-container">
<img src="images/${area.image}" alt="" class="w-full h-full object-cover pointer-events-none">
<!-- Attribution Tooltip -->
<div id="area-bg-attribution" class="absolute bottom-4 right-4 bg-black bg-opacity-75 text-white text-xs px-2 py-1 rounded opacity-0 transition-opacity duration-200 max-w-xs z-50">
<a href="${area.imageSourceUrl}" target="_blank" class="text-blue-300 hover:text-blue-100">
${area.imageAttribution}
</a>
</div>
</div>
<!-- Overview Section -->
<section id="overview" class="mb-12 relative z-20">
<!-- Overview Background Image -->
<div class="absolute inset-0 opacity-10 rounded-lg overflow-hidden">
<img src="images/${area.image}" alt="" class="w-full h-full object-cover">
</div>
<div class="bg-white bg-opacity-90 backdrop-blur-sm rounded-lg shadow-sm p-8 mb-6 relative z-10">
<h1 class="text-3xl font-bold text-gray-900 mb-6">${area.title}</h1>
<!-- Description -->
<div class="mb-8">
<p class="text-gray-700 leading-relaxed mb-6">${area.description}</p>
</div>
<!-- Role of Openness -->
<div class="mb-8 px-4 py-4 bg-gradient-to-r from-orange-50 to-yellow-50 border-l-4 border-orange-300 rounded-r-lg">
<p class="font-bold text-orange-900 mb-2">πŸ€— The Role of Openness</p>
<p class="text-orange-800 italic leading-relaxed">${area.openness}</p>
</div>
<!-- Sub-areas Navigation -->
<div class="mb-8">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Explore Sub-areas</h3>
<div class="grid grid-cols-1 md:grid-cols-${Math.min(subAreas.length, 3)} gap-4">
${subAreas.map(subArea => `
<a
href="#${subArea.id}"
class="group p-4 bg-gradient-to-br ${subArea.gradient || 'from-gray-50 to-gray-100 hover:from-gray-100 hover:to-gray-200 border-gray-200 hover:border-gray-300 text-gray-900'} rounded-lg border transition-all duration-200 text-left block no-underline"
>
<h4 class="font-semibold text-sm group-hover:scale-105 transition-transform">${subArea.name}</h4>
</a>
`).join('')}
</div>
</div>
</div>
</section>
<!-- Sub-areas Sections -->
${subAreas.map(subArea => `
<section id="${subArea.id}" class="mb-12 relative z-20">
<div class="bg-white bg-opacity-90 backdrop-blur-sm rounded-lg shadow-sm p-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">${subArea.name}</h2>
${subArea.description ? `
<div class="mb-6">
<p class="text-gray-700 leading-relaxed">${subArea.description}</p>
</div>
` : ''}
${subArea.openness ? `
<div class="mb-6 px-4 py-4 bg-gradient-to-r from-orange-50 to-yellow-50 border-l-4 border-orange-300 rounded-r-lg">
<p class="font-bold text-orange-900 mb-2">πŸ€— The Role of Openness</p>
<p class="text-orange-800 italic leading-relaxed">${subArea.openness}</p>
</div>
` : ''}
<!-- Artifacts Carousel -->
<div class="mb-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Related Research & Resources</h3>
<div id="${subArea.id}-artifacts-carousel">
<!-- Carousel will be inserted here -->
</div>
</div>
</div>
</section>
`).join('')}
<!-- Resources Section -->
<section id="resources" class="mb-12 relative z-10">
<div class="bg-white bg-opacity-90 backdrop-blur-sm rounded-lg shadow-sm p-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Technical Resources</h2>
<!-- Filter resources by area -->
<div class="mb-6">
<p class="text-gray-700 leading-relaxed">
Technical resources and tools related to ${area.name.toLowerCase()} research and development.
</p>
</div>
<!-- Resource Cards Carousel -->
<div class="relative">
<div class="resource-carousel-container overflow-hidden" style="width: calc(100% - 2rem); margin: 0 auto;">
<div class="resource-carousel flex gap-6 transition-transform duration-300 ease-in-out" style="width: ${areaResources.length * 624}px;">
${areaResources.map(resource => createResourceCard(resource)).join('')}
</div>
</div>
<!-- Carousel Navigation -->
<div class="flex justify-center mt-4 space-x-2">
<button class="resource-carousel-prev bg-gray-200 hover:bg-gray-300 text-gray-700 px-3 py-2 rounded-md transition-colors">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
</svg>
</button>
<button class="resource-carousel-next bg-gray-200 hover:bg-gray-300 text-gray-700 px-3 py-2 rounded-md transition-colors">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</button>
</div>
</div>
</div>
</section>
`;
return {
content,
init: () => {
// Initialize artifact carousels for each sub-area with a small delay
// to ensure DOM elements are fully rendered
setTimeout(() => {
subAreas.forEach(subArea => {
// For now, show all artifacts in each carousel
// Later this will be filtered by the backend
const carouselId = `${subArea.id}-artifacts-carousel`;
createArtifactCarousel(featuredArtifacts, carouselId);
});
}, 50);
// Initialize resource cards
initializeResourceCards();
// Initialize resource carousel if there are resources
if (areaResources.length > 0) {
initializeAreaResourceCarousel(areaResources);
}
// Initialize background attribution
initializeAreaBackgroundAttribution();
}
};
}
export function getAreaPageSidebar(areaId) {
const area = areasData[areaId];
if (!area) {
return '<div class="p-4">Area not found</div>';
}
const subAreas = Object.entries(area.subAreas).map(([key, value]) => ({
id: key,
name: typeof value === 'string' ? value : value.name
}));
return `
<nav class="p-4">
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">On This Page</h3>
<ul class="space-y-1">
<li><a href="#overview" class="page-nav-link block px-3 py-2 text-sm text-blue-600 bg-blue-50 rounded-md">Overview</a></li>
<li class="ml-4">
<ul class="space-y-1">
${subAreas.map(subArea => `
<li><a href="#${subArea.id}" class="page-nav-link block px-3 py-2 text-xs text-gray-600 hover:text-gray-800 hover:bg-gray-50 rounded-md transition-colors">${subArea.name}</a></li>
`).join('')}
</ul>
</li>
<li><a href="#resources" class="page-nav-link block px-3 py-2 text-sm text-gray-700 hover:text-gray-900 hover:bg-gray-50 rounded-md transition-colors">Resources</a></li>
</ul>
</nav>
`;
}
function initializeAreaResourceCarousel(resources) {
const carousel = document.querySelector('.resource-carousel');
const prevButton = document.querySelector('.resource-carousel-prev');
const nextButton = document.querySelector('.resource-carousel-next');
if (!carousel || !prevButton || !nextButton) {
return;
}
let currentIndex = 0;
const cardWidth = 624; // 600px card + 24px gap
const visibleCards = 1.5; // Show 1.5 cards at a time (larger cards)
const maxIndex = resources.length - 1; // Allow going to the last item
function updateCarousel() {
const translateX = -currentIndex * cardWidth;
carousel.style.transform = `translateX(${translateX}px)`;
// Update button states - no disabled states for looping
prevButton.classList.remove('opacity-50', 'cursor-not-allowed');
nextButton.classList.remove('opacity-50', 'cursor-not-allowed');
}
prevButton.addEventListener('click', () => {
currentIndex = currentIndex > 0 ? currentIndex - 1 : maxIndex;
updateCarousel();
});
nextButton.addEventListener('click', () => {
currentIndex = currentIndex < maxIndex ? currentIndex + 1 : 0;
updateCarousel();
});
// Initialize carousel state
updateCarousel();
}
function initializeAreaBackgroundAttribution() {
const backgroundContainer = document.getElementById('area-background-container');
const attribution = document.getElementById('area-bg-attribution');
if (!backgroundContainer || !attribution) {
return;
}
// Show attribution on hover over the background container
backgroundContainer.addEventListener('mouseenter', () => {
attribution.style.opacity = '1';
});
backgroundContainer.addEventListener('mouseleave', () => {
attribution.style.opacity = '0';
});
}
// Colors are now defined in the areas data and accessed via subArea.gradient