@@ -3,13 +3,16 @@ const activityFeedContainer = document.getElementById(ACITIVITY_FEED_CONTAINER);
3
3
const activityList = document . querySelector ( '.activity-list' ) ;
4
4
const tabsList = document . querySelector ( '.tabs' ) ;
5
5
const lastElementContainer = document . querySelector ( LAST_ELEMENT_CONTAINER ) ;
6
+ const usernameInput = document . getElementById ( 'assignee-search' ) ;
7
+ const clearUsernameBtn = document . getElementById ( 'clear-username' ) ;
6
8
7
9
let query = { } ;
8
10
let newLink = '' ;
9
11
let activityFeedPage = 0 ;
10
12
let nextLink = '' ;
11
13
let isDataLoading = false ;
12
14
let category = CATEGORY . ALL ;
15
+ let activeIndex = - 1 ;
13
16
14
17
const tabsData = [
15
18
{ name : 'All' , 'data-type' : CATEGORY . ALL , class : 'active' } ,
@@ -21,7 +24,7 @@ const tabsData = [
21
24
22
25
async function renderFeed ( ) {
23
26
changeFilter ( ) ;
24
- await populateActivityFeed ( { category } ) ;
27
+ await populateActivityFeed ( { category : currentCategory , ... activeFilters } ) ;
25
28
addIntersectionObserver ( ) ;
26
29
}
27
30
@@ -38,9 +41,9 @@ function createTabListItem(tab) {
38
41
function handleTabClick ( tab ) {
39
42
tabs . forEach ( ( t ) => t . classList . remove ( 'active' ) ) ;
40
43
tab . classList . add ( 'active' ) ;
41
- const category = tab . dataset . type ;
42
- changeFilter ( ) ;
43
- populateActivityFeed ( { category } ) ;
44
+ currentCategory = tab . dataset . type ;
45
+
46
+ refreshFeed ( ) ;
44
47
}
45
48
46
49
tabsData . forEach ( ( tab ) => {
@@ -300,16 +303,30 @@ function formatTaskRequestsLog(data) {
300
303
async function populateActivityFeed ( query = { } , newLink ) {
301
304
activityFeedPage ++ ;
302
305
const currentVersion = activityFeedPage ;
306
+
307
+ const combinedQuery = { ...query , ...activeFilters } ;
308
+
303
309
try {
304
310
isDataLoading = true ;
305
311
addLoader ( container ) ;
306
- const activityFeedData = await getActivityFeedData ( query , newLink ) ;
312
+
313
+ const activityFeedData = await getActivityFeedData ( combinedQuery , newLink ) ;
314
+
315
+ activityFeedContainer . innerHTML = '' ;
316
+
307
317
if ( activityFeedData ) {
308
318
nextLink = activityFeedData . next ;
309
319
const allActivityFeedData = activityFeedData . data ;
320
+
310
321
if ( currentVersion !== activityFeedPage ) {
311
322
return ;
312
323
}
324
+
325
+ if ( allActivityFeedData . length === 0 ) {
326
+ addEmptyPageMessage ( activityFeedContainer ) ;
327
+ return ;
328
+ }
329
+
313
330
for ( const data of allActivityFeedData ) {
314
331
const renderedItem = renderActivityItem ( data ) ;
315
332
activityFeedContainer . appendChild ( renderedItem ) ;
@@ -319,6 +336,7 @@ async function populateActivityFeed(query = {}, newLink) {
319
336
showMessage ( activityFeedContainer , error ) ;
320
337
} finally {
321
338
if ( currentVersion !== activityFeedPage ) return ;
339
+
322
340
removeLoader ( 'loader' ) ;
323
341
isDataLoading = false ;
324
342
}
@@ -335,7 +353,6 @@ async function getActivityFeedData(query = {}, nextLink) {
335
353
'Content-type' : 'application/json' ,
336
354
} ,
337
355
} ) ;
338
-
339
356
try {
340
357
const res = await fetch ( finalUrl , {
341
358
credentials : 'include' ,
@@ -370,5 +387,160 @@ async function getActivityFeedData(query = {}, nextLink) {
370
387
}
371
388
}
372
389
390
+ let currentCategory = CATEGORY . ALL ;
391
+
392
+ function handleTabClick ( tab ) {
393
+ tabs . forEach ( ( t ) => t . classList . remove ( 'active' ) ) ;
394
+ tab . classList . add ( 'active' ) ;
395
+ currentCategory = tab . dataset . type ;
396
+ changeFilter ( ) ;
397
+ populateActivityFeed ( { category : currentCategory } ) ;
398
+ }
399
+
400
+ let activeFilters = {
401
+ username : null ,
402
+ startDate : null ,
403
+ endDate : null ,
404
+ } ;
405
+
406
+ document . getElementById ( 'start-date' ) . addEventListener ( 'change' , applyFilter ) ;
407
+ document . getElementById ( 'end-date' ) . addEventListener ( 'change' , applyFilter ) ;
408
+ clearUsernameBtn . addEventListener ( 'click' , clearUsernameFilter ) ;
409
+
410
+ clearUsernameBtn . style . display = 'none' ;
411
+
412
+ usernameInput . addEventListener ( 'input' , function ( ) {
413
+ if ( usernameInput . value . trim ( ) !== '' ) {
414
+ clearUsernameBtn . style . display = 'inline' ;
415
+ } else {
416
+ clearUsernameBtn . style . display = 'none' ;
417
+ }
418
+ } ) ;
419
+
420
+ function applyFilter ( ) {
421
+ const username = document . getElementById ( 'assignee-search' ) . value . trim ( ) ;
422
+ const startDate = document . getElementById ( 'start-date' ) . value ;
423
+ const endDate = document . getElementById ( 'end-date' ) . value ;
424
+
425
+ if ( startDate && endDate && new Date ( startDate ) > new Date ( endDate ) ) {
426
+ alert ( 'Start Date cannot be later than End Date!' ) ;
427
+ return ;
428
+ }
429
+
430
+ activeFilters . username = username || null ;
431
+ activeFilters . startDate = startDate
432
+ ? new Date ( startDate ) . toISOString ( )
433
+ : null ;
434
+ activeFilters . endDate = endDate ? new Date ( endDate ) . toISOString ( ) : null ;
435
+
436
+ populateActivityFeed ( { category : currentCategory , ...activeFilters } ) ;
437
+ }
438
+
439
+ function clearUsernameFilter ( ) {
440
+ const usernameInput = document . getElementById ( 'assignee-search' ) ;
441
+ const suggestionBox = document . getElementById ( 'suggestion-box' ) ;
442
+ const clearUsernameBtn = document . getElementById ( 'clear-username' ) ;
443
+
444
+ usernameInput . value = '' ;
445
+ suggestionBox . style . display = 'none' ;
446
+ clearUsernameBtn . style . display = 'none' ;
447
+
448
+ activeFilters . username = null ;
449
+ populateActivityFeed ( { category : currentCategory , ...activeFilters } ) ;
450
+ }
451
+
452
+ async function fetchSuggestions ( ) {
453
+ const input = document . getElementById ( 'assignee-search' ) ;
454
+ const query = input . value . trim ( ) ;
455
+ const suggestionBox = document . getElementById ( 'suggestion-box' ) ;
456
+
457
+ if ( ! query ) {
458
+ suggestionBox . style . display = 'none' ;
459
+ return ;
460
+ }
461
+
462
+ try {
463
+ const response = await fetch ( `${ API_BASE_URL } /users?search=${ query } ` , {
464
+ method : 'GET' ,
465
+ credentials : 'include' ,
466
+ headers : {
467
+ 'Content-Type' : 'application/json' ,
468
+ } ,
469
+ } ) ;
470
+
471
+ if ( response . ok ) {
472
+ const data = await response . json ( ) ;
473
+ const users = data . users || [ ] ;
474
+ if ( users . length > 0 ) {
475
+ renderSuggestions ( users ) ;
476
+ suggestionBox . style . display = 'block' ;
477
+ } else {
478
+ suggestionBox . innerHTML =
479
+ '<div class="suggestion-item">No users found</div>' ;
480
+ suggestionBox . style . display = 'block' ;
481
+ }
482
+ } else {
483
+ console . error ( 'Error fetching suggestions:' , response . statusText ) ;
484
+ }
485
+ } catch ( error ) {
486
+ console . error ( 'Error:' , error ) ;
487
+ }
488
+ }
489
+
490
+ function renderSuggestions ( users ) {
491
+ const suggestionBox = document . getElementById ( 'suggestion-box' ) ;
492
+ suggestionBox . innerHTML = users
493
+ . map ( ( user , index ) => {
494
+ const userIcon = `<img src="/feed/assets/user.svg" alt="User Icon" class="user-icon" />` ;
495
+ return `<div
496
+ class="suggestion-item ${
497
+ index === activeIndex ? 'active' : ''
498
+ } "
499
+ onclick="selectAssignee('${ user . username } ')">
500
+ <div class="suggestion-content">
501
+ ${ userIcon }
502
+ <span>${ user . username } </span>
503
+ </div>
504
+ </div>` ;
505
+ } )
506
+ . join ( '' ) ;
507
+ }
508
+
509
+ function selectAssignee ( username ) {
510
+ const input = document . getElementById ( 'assignee-search' ) ;
511
+ input . value = username ;
512
+ const suggestionBox = document . getElementById ( 'suggestion-box' ) ;
513
+ suggestionBox . style . display = 'none' ;
514
+ applyFilter ( ) ;
515
+ }
516
+
517
+ document . getElementById ( 'assignee-search' ) . addEventListener ( 'keydown' , ( e ) => {
518
+ const suggestionBox = document . getElementById ( 'suggestion-box' ) ;
519
+ const items = suggestionBox . querySelectorAll ( '.suggestion-item' ) ;
520
+
521
+ if ( e . key === 'ArrowDown' ) {
522
+ e . preventDefault ( ) ;
523
+ activeIndex = ( activeIndex + 1 ) % items . length ;
524
+ } else if ( e . key === 'ArrowUp' ) {
525
+ e . preventDefault ( ) ;
526
+ activeIndex = ( activeIndex - 1 + items . length ) % items . length ;
527
+ } else if ( e . key === 'Enter' ) {
528
+ e . preventDefault ( ) ;
529
+ if ( activeIndex >= 0 && activeIndex < items . length ) {
530
+ items [ activeIndex ] . click ( ) ;
531
+ }
532
+ } else if ( e . key === 'Escape' ) {
533
+ suggestionBox . style . display = 'none' ;
534
+ }
535
+
536
+ items . forEach ( ( item , index ) => {
537
+ if ( index === activeIndex ) {
538
+ item . classList . add ( 'active' ) ;
539
+ } else {
540
+ item . classList . remove ( 'active' ) ;
541
+ }
542
+ } ) ;
543
+ } ) ;
544
+
373
545
// main entry
374
546
renderFeed ( ) ;
0 commit comments