danielrosehill's picture
commit
323ad41
// Main application state
let allProjects = [];
let filteredProjects = [];
let currentPlatformFilter = 'all';
let currentUsecaseFilter = 'all';
let currentSearchQuery = '';
let currentSort = 'name';
// Load projects data
async function loadProjects() {
try {
const response = await fetch('projects.json');
allProjects = await response.json();
filteredProjects = [...allProjects];
renderProjects();
updateProjectCount();
} catch (error) {
console.error('Error loading projects:', error);
document.getElementById('projects-grid').innerHTML = `
<div class="no-results">
<h3>Error loading projects</h3>
<p>Unable to load the projects data. Please try refreshing the page.</p>
</div>
`;
}
}
// Apply all filters
function applyFilters() {
filteredProjects = allProjects.filter(project => {
// Platform filter
const platformMatch = currentPlatformFilter === 'all' ||
project.platforms.includes(currentPlatformFilter);
// Use case filter
const usecaseMatch = currentUsecaseFilter === 'all' ||
project.usecases.includes(currentUsecaseFilter);
// Search query
const searchMatch = currentSearchQuery === '' ||
project.name.toLowerCase().includes(currentSearchQuery.toLowerCase()) ||
project.description.toLowerCase().includes(currentSearchQuery.toLowerCase());
return platformMatch && usecaseMatch && searchMatch;
});
sortProjects();
renderProjects();
updateProjectCount();
}
// Sort projects
function sortProjects() {
switch (currentSort) {
case 'name':
filteredProjects.sort((a, b) => a.name.localeCompare(b.name));
break;
case 'platform':
filteredProjects.sort((a, b) => {
const platformA = a.platforms[0] || '';
const platformB = b.platforms[0] || '';
return platformA.localeCompare(platformB);
});
break;
case 'usecase':
filteredProjects.sort((a, b) => {
const usecaseA = a.usecases[0] || '';
const usecaseB = b.usecases[0] || '';
return usecaseA.localeCompare(usecaseB);
});
break;
}
}
// Render projects to the grid
function renderProjects() {
const grid = document.getElementById('projects-grid');
if (filteredProjects.length === 0) {
grid.innerHTML = `
<div class="no-results">
<h3>No projects found</h3>
<p>Try adjusting your filters or search query.</p>
</div>
`;
return;
}
grid.innerHTML = filteredProjects.map(project => createProjectCard(project)).join('');
}
// Create a project card HTML
function createProjectCard(project) {
const platformTags = project.platforms
.map(p => `<span class="meta-tag platform-tag">${formatTag(p)}</span>`)
.join('');
const usecaseTags = project.usecases
.map(u => `<span class="meta-tag usecase-tag">${formatTag(u)}</span>`)
.join('');
const starBadge = project.github_repo
? `<img src="https://img.shields.io/github/stars/${project.github_repo}?style=flat-square" alt="GitHub stars" class="github-stars-badge">`
: '';
return `
<div class="project-card">
<h3>
<a href="${project.url}" target="_blank" rel="noopener">${escapeHtml(project.name)}</a>
${starBadge}
</h3>
<p class="project-description">${escapeHtml(project.description)}</p>
<div class="project-meta">
${platformTags}
${usecaseTags}
</div>
</div>
`;
}
// Format tag names for display
function formatTag(tag) {
return tag
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
// Escape HTML to prevent XSS
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Update project count display
function updateProjectCount() {
const count = document.getElementById('project-count');
count.textContent = `(${filteredProjects.length} of ${allProjects.length})`;
}
// Set up event listeners
function setupEventListeners() {
// Platform filter buttons
document.querySelectorAll('#platform-filters .filter-btn').forEach(btn => {
btn.addEventListener('click', () => {
// Remove active class from all platform buttons
document.querySelectorAll('#platform-filters .filter-btn').forEach(b => {
b.classList.remove('active');
});
// Add active class to clicked button
btn.classList.add('active');
// Update filter
currentPlatformFilter = btn.dataset.filter;
applyFilters();
});
});
// Use case filter buttons
document.querySelectorAll('#usecase-filters .filter-btn').forEach(btn => {
btn.addEventListener('click', () => {
// Remove active class from all usecase buttons
document.querySelectorAll('#usecase-filters .filter-btn').forEach(b => {
b.classList.remove('active');
});
// Add active class to clicked button
btn.classList.add('active');
// Update filter
currentUsecaseFilter = btn.dataset.filter;
applyFilters();
});
});
// Search input
const searchInput = document.getElementById('search-input');
searchInput.addEventListener('input', (e) => {
currentSearchQuery = e.target.value;
applyFilters();
});
// Sort select
const sortSelect = document.getElementById('sort-select');
sortSelect.addEventListener('change', (e) => {
currentSort = e.target.value;
sortProjects();
renderProjects();
});
}
// Initialize the application
document.addEventListener('DOMContentLoaded', () => {
setupEventListeners();
loadProjects();
});