Skip to content

Commit 3ac2c94

Browse files
feat: add export functionality for filtered users in JSON and CSV formats
1 parent 010c84a commit 3ac2c94

3 files changed

Lines changed: 101 additions & 0 deletions

File tree

docs/script.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,75 @@ function applyFilters() {
251251
});
252252
}
253253

254+
// Export JSON
255+
function exportFilteredJSON() {
256+
257+
if (!filteredUsers.length) {
258+
alert('No users to export');
259+
return;
260+
}
261+
262+
const userData = filteredUsers.map(user => user.raw);
263+
264+
const jsonString = JSON.stringify(userData, null, 2);
265+
const blob = new Blob([jsonString], {type: 'application/json'});
266+
267+
const url = URL.createObjectURL(blob);
268+
269+
const a = document.createElement('a');
270+
a.href = url;
271+
a.download = 'github-faces.json';
272+
273+
document.body.appendChild(a);
274+
a.click();
275+
document.body.removeChild(a);
276+
277+
URL.revokeObjectURL(url);
278+
}
279+
280+
// Export CSV
281+
function exportFilteredCSV() {
282+
283+
if (!filteredUsers.length) {
284+
alert('No users to export');
285+
return;
286+
}
287+
288+
const rows = filteredUsers.map(user => user.raw);
289+
const headers = Object.keys(rows[0]);
290+
291+
const escapeCSV = value => {
292+
if (value == null) return '';
293+
// if object or array, stringify it
294+
const str =
295+
typeof value === 'object'
296+
? JSON.stringify(value).replace(/"/g, '""')
297+
: String(value).replace(/"/g, '""');
298+
299+
return `"${str}"`;
300+
}
301+
302+
303+
const csv = [
304+
headers.join(','),
305+
...rows.map(row => headers.map(h => escapeCSV(row[h])).join(','))
306+
].join('\n');
307+
308+
const blob = new Blob([csv], {type: 'text/csv;charset=utf-8'});
309+
310+
const url = URL.createObjectURL(blob);
311+
312+
const a = document.createElement('a');
313+
a.href = url;
314+
a.download = 'github-faces.csv';
315+
316+
document.body.appendChild(a);
317+
a.click();
318+
document.body.removeChild(a);
319+
320+
URL.revokeObjectURL(url);
321+
}
322+
254323
/**
255324
* Get all active filter values from DOM
256325
* @returns {Object} Active filter values

docs/styles.css

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,24 @@ h1 {
248248
color: #667eea;
249249
}
250250

251+
.export-buttons {
252+
display: flex;
253+
gap: 10px;
254+
justify-content: center;
255+
}
256+
257+
.filters-buttons,
258+
.export-buttons {
259+
width: 100%;
260+
}
261+
262+
.filters-buttons .btn-apply,
263+
.filters-buttons .btn-reset,
264+
.export-buttons .btn-apply {
265+
flex: 1;
266+
}
267+
268+
251269
.btn-random {
252270
width: 100%;
253271
padding: 12px;
@@ -591,6 +609,15 @@ footer {
591609
order: -1;
592610
}
593611

612+
.export-buttons {
613+
width: 100%;
614+
}
615+
616+
.export-buttons .btn-apply {
617+
flex: 1;
618+
padding: 12px;
619+
}
620+
594621
.main-content {
595622
grid-column: 1;
596623
grid-row: 2;

layouts/layout.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,11 @@ <h2>🔍 Find Your Favorite Developer</h2>
265265
<button class="btn-reset" onclick="resetFilters()">Reset</button>
266266
</div>
267267

268+
<div class="filters-buttons export-buttons">
269+
<button class="btn-apply" onclick="exportFilteredJSON()">Export JSON</button>
270+
<button class="btn-apply" onclick="exportFilteredCSV()">Export CSV</button>
271+
</div>
272+
268273
<div class="results-info loading-state" id="loadingState" style="display: none;">
269274
<div class="loading-spinner"></div>
270275
<p>Loading developers...</p>

0 commit comments

Comments
 (0)