Skip to content

Commit

Permalink
Merge branch 'develop' into lazy_loading_test
Browse files Browse the repository at this point in the history
  • Loading branch information
Achintya-Chatterjee authored Jan 31, 2025
2 parents d00080a + c74e1f4 commit ff44cec
Show file tree
Hide file tree
Showing 5 changed files with 465 additions and 38 deletions.
3 changes: 3 additions & 0 deletions feed/assets/user.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 34 additions & 2 deletions feed/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,46 @@
<body>
<nav id="tasksNav"></nav>
<h1 id="pageTitle">Activity Feed</h1>
<nav class="tabs-container">
<div class="filters">
<div class="filter-row">
<div class="input-wrapper">
<label for="assignee-search">Username</label>
<input
type="text"
id="assignee-search"
placeholder="Enter Username"
oninput="fetchSuggestions()"
/>
<span
id="clear-username"
class="clear-icon"
onclick="clearUsernameFilter()"
>×</span
>
<div id="suggestion-box" class="suggestion-box"></div>
</div>

<div class="date-filters">
<div class="date-inputs">
<label for="start-date">Start Date</label>
<input type="date" id="start-date" placeholder="Start Date" />
</div>
<div class="date-inputs">
<label for="end-date">End Date</label>
<input type="date" id="end-date" placeholder="End Date" />
</div>
</div>
</div>
</div>

<section class="tabs-container">
<ul class="tabs"></ul>
<div class="container">
<div id="activity_feed_container">
<ul class="activity-list"></ul>
</div>
<div class="virtual"></div>
</div>
</nav>
</section>
</body>
</html>
184 changes: 178 additions & 6 deletions feed/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ const activityFeedContainer = document.getElementById(ACITIVITY_FEED_CONTAINER);
const activityList = document.querySelector('.activity-list');
const tabsList = document.querySelector('.tabs');
const lastElementContainer = document.querySelector(LAST_ELEMENT_CONTAINER);
const usernameInput = document.getElementById('assignee-search');
const clearUsernameBtn = document.getElementById('clear-username');

let query = {};
let newLink = '';
let activityFeedPage = 0;
let nextLink = '';
let isDataLoading = false;
let category = CATEGORY.ALL;
let activeIndex = -1;

const tabsData = [
{ name: 'All', 'data-type': CATEGORY.ALL, class: 'active' },
Expand All @@ -21,7 +24,7 @@ const tabsData = [

async function renderFeed() {
changeFilter();
await populateActivityFeed({ category });
await populateActivityFeed({ category: currentCategory, ...activeFilters });
addIntersectionObserver();
}

Expand All @@ -38,9 +41,9 @@ function createTabListItem(tab) {
function handleTabClick(tab) {
tabs.forEach((t) => t.classList.remove('active'));
tab.classList.add('active');
const category = tab.dataset.type;
changeFilter();
populateActivityFeed({ category });
currentCategory = tab.dataset.type;

refreshFeed();
}

tabsData.forEach((tab) => {
Expand Down Expand Up @@ -300,16 +303,30 @@ function formatTaskRequestsLog(data) {
async function populateActivityFeed(query = {}, newLink) {
activityFeedPage++;
const currentVersion = activityFeedPage;

const combinedQuery = { ...query, ...activeFilters };

try {
isDataLoading = true;
addLoader(container);
const activityFeedData = await getActivityFeedData(query, newLink);

const activityFeedData = await getActivityFeedData(combinedQuery, newLink);

activityFeedContainer.innerHTML = '';

if (activityFeedData) {
nextLink = activityFeedData.next;
const allActivityFeedData = activityFeedData.data;

if (currentVersion !== activityFeedPage) {
return;
}

if (allActivityFeedData.length === 0) {
addEmptyPageMessage(activityFeedContainer);
return;
}

for (const data of allActivityFeedData) {
const renderedItem = renderActivityItem(data);
activityFeedContainer.appendChild(renderedItem);
Expand All @@ -319,6 +336,7 @@ async function populateActivityFeed(query = {}, newLink) {
showMessage(activityFeedContainer, error);
} finally {
if (currentVersion !== activityFeedPage) return;

removeLoader('loader');
isDataLoading = false;
}
Expand All @@ -335,7 +353,6 @@ async function getActivityFeedData(query = {}, nextLink) {
'Content-type': 'application/json',
},
});

try {
const res = await fetch(finalUrl, {
credentials: 'include',
Expand Down Expand Up @@ -370,5 +387,160 @@ async function getActivityFeedData(query = {}, nextLink) {
}
}

let currentCategory = CATEGORY.ALL;

function handleTabClick(tab) {
tabs.forEach((t) => t.classList.remove('active'));
tab.classList.add('active');
currentCategory = tab.dataset.type;
changeFilter();
populateActivityFeed({ category: currentCategory });
}

let activeFilters = {
username: null,
startDate: null,
endDate: null,
};

document.getElementById('start-date').addEventListener('change', applyFilter);
document.getElementById('end-date').addEventListener('change', applyFilter);
clearUsernameBtn.addEventListener('click', clearUsernameFilter);

clearUsernameBtn.style.display = 'none';

usernameInput.addEventListener('input', function () {
if (usernameInput.value.trim() !== '') {
clearUsernameBtn.style.display = 'inline';
} else {
clearUsernameBtn.style.display = 'none';
}
});

function applyFilter() {
const username = document.getElementById('assignee-search').value.trim();
const startDate = document.getElementById('start-date').value;
const endDate = document.getElementById('end-date').value;

if (startDate && endDate && new Date(startDate) > new Date(endDate)) {
alert('Start Date cannot be later than End Date!');
return;
}

activeFilters.username = username || null;
activeFilters.startDate = startDate
? new Date(startDate).toISOString()
: null;
activeFilters.endDate = endDate ? new Date(endDate).toISOString() : null;

populateActivityFeed({ category: currentCategory, ...activeFilters });
}

function clearUsernameFilter() {
const usernameInput = document.getElementById('assignee-search');
const suggestionBox = document.getElementById('suggestion-box');
const clearUsernameBtn = document.getElementById('clear-username');

usernameInput.value = '';
suggestionBox.style.display = 'none';
clearUsernameBtn.style.display = 'none';

activeFilters.username = null;
populateActivityFeed({ category: currentCategory, ...activeFilters });
}

async function fetchSuggestions() {
const input = document.getElementById('assignee-search');
const query = input.value.trim();
const suggestionBox = document.getElementById('suggestion-box');

if (!query) {
suggestionBox.style.display = 'none';
return;
}

try {
const response = await fetch(`${API_BASE_URL}/users?search=${query}`, {
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
});

if (response.ok) {
const data = await response.json();
const users = data.users || [];
if (users.length > 0) {
renderSuggestions(users);
suggestionBox.style.display = 'block';
} else {
suggestionBox.innerHTML =
'<div class="suggestion-item">No users found</div>';
suggestionBox.style.display = 'block';
}
} else {
console.error('Error fetching suggestions:', response.statusText);
}
} catch (error) {
console.error('Error:', error);
}
}

function renderSuggestions(users) {
const suggestionBox = document.getElementById('suggestion-box');
suggestionBox.innerHTML = users
.map((user, index) => {
const userIcon = `<img src="/feed/assets/user.svg" alt="User Icon" class="user-icon" />`;
return `<div
class="suggestion-item ${
index === activeIndex ? 'active' : ''
}"
onclick="selectAssignee('${user.username}')">
<div class="suggestion-content">
${userIcon}
<span>${user.username}</span>
</div>
</div>`;
})
.join('');
}

function selectAssignee(username) {
const input = document.getElementById('assignee-search');
input.value = username;
const suggestionBox = document.getElementById('suggestion-box');
suggestionBox.style.display = 'none';
applyFilter();
}

document.getElementById('assignee-search').addEventListener('keydown', (e) => {
const suggestionBox = document.getElementById('suggestion-box');
const items = suggestionBox.querySelectorAll('.suggestion-item');

if (e.key === 'ArrowDown') {
e.preventDefault();
activeIndex = (activeIndex + 1) % items.length;
} else if (e.key === 'ArrowUp') {
e.preventDefault();
activeIndex = (activeIndex - 1 + items.length) % items.length;
} else if (e.key === 'Enter') {
e.preventDefault();
if (activeIndex >= 0 && activeIndex < items.length) {
items[activeIndex].click();
}
} else if (e.key === 'Escape') {
suggestionBox.style.display = 'none';
}

items.forEach((item, index) => {
if (index === activeIndex) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
});

// main entry
renderFeed();
Loading

0 comments on commit ff44cec

Please sign in to comment.