| |
| let allProjects = []; |
| let filteredProjects = []; |
| let currentPlatformFilter = 'all'; |
| let currentUsecaseFilter = 'all'; |
| let currentSearchQuery = ''; |
| let currentSort = 'name'; |
|
|
| |
| 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> |
| `; |
| } |
| } |
|
|
| |
| function applyFilters() { |
| filteredProjects = allProjects.filter(project => { |
| |
| const platformMatch = currentPlatformFilter === 'all' || |
| project.platforms.includes(currentPlatformFilter); |
|
|
| |
| const usecaseMatch = currentUsecaseFilter === 'all' || |
| project.usecases.includes(currentUsecaseFilter); |
|
|
| |
| const searchMatch = currentSearchQuery === '' || |
| project.name.toLowerCase().includes(currentSearchQuery.toLowerCase()) || |
| project.description.toLowerCase().includes(currentSearchQuery.toLowerCase()); |
|
|
| return platformMatch && usecaseMatch && searchMatch; |
| }); |
|
|
| sortProjects(); |
| renderProjects(); |
| updateProjectCount(); |
| } |
|
|
| |
| 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; |
| } |
| } |
|
|
| |
| 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(''); |
| } |
|
|
| |
| 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> |
| `; |
| } |
|
|
| |
| function formatTag(tag) { |
| return tag |
| .split('-') |
| .map(word => word.charAt(0).toUpperCase() + word.slice(1)) |
| .join(' '); |
| } |
|
|
| |
| function escapeHtml(text) { |
| const div = document.createElement('div'); |
| div.textContent = text; |
| return div.innerHTML; |
| } |
|
|
| |
| function updateProjectCount() { |
| const count = document.getElementById('project-count'); |
| count.textContent = `(${filteredProjects.length} of ${allProjects.length})`; |
| } |
|
|
| |
| function setupEventListeners() { |
| |
| document.querySelectorAll('#platform-filters .filter-btn').forEach(btn => { |
| btn.addEventListener('click', () => { |
| |
| document.querySelectorAll('#platform-filters .filter-btn').forEach(b => { |
| b.classList.remove('active'); |
| }); |
|
|
| |
| btn.classList.add('active'); |
|
|
| |
| currentPlatformFilter = btn.dataset.filter; |
| applyFilters(); |
| }); |
| }); |
|
|
| |
| document.querySelectorAll('#usecase-filters .filter-btn').forEach(btn => { |
| btn.addEventListener('click', () => { |
| |
| document.querySelectorAll('#usecase-filters .filter-btn').forEach(b => { |
| b.classList.remove('active'); |
| }); |
|
|
| |
| btn.classList.add('active'); |
|
|
| |
| currentUsecaseFilter = btn.dataset.filter; |
| applyFilters(); |
| }); |
| }); |
|
|
| |
| const searchInput = document.getElementById('search-input'); |
| searchInput.addEventListener('input', (e) => { |
| currentSearchQuery = e.target.value; |
| applyFilters(); |
| }); |
|
|
| |
| const sortSelect = document.getElementById('sort-select'); |
| sortSelect.addEventListener('change', (e) => { |
| currentSort = e.target.value; |
| sortProjects(); |
| renderProjects(); |
| }); |
| } |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| setupEventListeners(); |
| loadProjects(); |
| }); |
|
|