|
|
|
|
|
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 (!topicId) { |
|
|
return renderAreaOverview(area, areaId); |
|
|
} |
|
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
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 || ''; |
|
|
|
|
|
|
|
|
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: () => { |
|
|
|
|
|
setTimeout(async () => { |
|
|
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 : featuredArtifacts, carouselId); |
|
|
} catch (error) { |
|
|
console.error(`Error creating carousel for ${topicId}:`, error); |
|
|
const carouselId = `${topicId}-artifacts-carousel`; |
|
|
createArtifactCarousel(featuredArtifacts, carouselId); |
|
|
} |
|
|
}, 50); |
|
|
} |
|
|
}; |
|
|
} |
|
|
|