File size: 6,283 Bytes
948605a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323ad41
 
948605a
 
 
 
 
 
323ad41
948605a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// 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();
});