diff --git a/__tests__/extension-requests/extension-requests.test.js b/__tests__/extension-requests/extension-requests.test.js
index b8d50c5e..f758b151 100644
--- a/__tests__/extension-requests/extension-requests.test.js
+++ b/__tests__/extension-requests/extension-requests.test.js
@@ -667,79 +667,6 @@ describe('Tests the Extension Requests Screen', () => {
expect(hasSkeletonClassAfter).toBe(false);
});
- it('checks whether the shimmer effect is working for deadlineValue element under feature flag', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const deadlineValueSelector = await page.$$(
- '[data-testid="skeleton-span"]',
- );
- expect(deadlineValueSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval('.tooltip-container', (el) =>
- el.classList.contains('skeleton-span'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
-
- it('checks whether the shimmer effect is working for requestedValue element under feature flag', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const requestedValueSelector = await page.$$(
- '[data-testid="skeleton-text"]',
- );
- expect(requestedValueSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval('.requested-day', (el) =>
- el.classList.contains('skeleton-text'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
- it('checks whether the shimmer effect is working for newDeadlineValue element under feature flag', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const newDeadlineValueSelector = await page.$$(
- '[data-testid="skeleton-span"]',
- );
- expect(newDeadlineValueSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval('.requested-day', (el) =>
- el.classList.contains('skeleton-span'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
-
- it('checks whether the shimmer effect is working for extensionRequestNumberValue element under feature flag', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const extensionRequestNumberValueSelector = await page.$$(
- '[data-testid="skeleton-span"]',
- );
- expect(extensionRequestNumberValueSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval(
- '.extension-request-number',
- (el) => el.classList.contains('skeleton-span'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
-
- it('checks whether the shimmer effect is visible under dev flag only for the statusSiteLink element', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const statusSiteLinkSelector = await page.$$(
- '[data-testid="external-link skeleton-link"]',
- );
- expect(statusSiteLinkSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval('.external-link', (el) =>
- el.classList.contains('skeleton-link'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
-
- it('checks whether the shimmer effect is visible under dev flag only for the taskStatusValue element', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const taskStatusValueElement = await page.$$(
- '[data-testid="skeleton-span"]',
- );
- expect(taskStatusValueElement).toBeTruthy();
- });
-
it('Checks whether the card is not removed from display when api call is unsuccessful', async () => {
const extensionCards = await page.$$('.extension-card');
diff --git a/__tests__/groups/group.test.js b/__tests__/groups/group.test.js
index 9830f377..828758d5 100644
--- a/__tests__/groups/group.test.js
+++ b/__tests__/groups/group.test.js
@@ -1,10 +1,21 @@
const puppeteer = require('puppeteer');
-const { allUsersData } = require('../../mock-data/users');
+const { allUsersData, superUserData } = require('../../mock-data/users');
const { discordGroups, GroupRoleData } = require('../../mock-data/groups');
const BASE_URL = 'https://api.realdevsquad.com';
const PAGE_URL = 'http://localhost:8000';
+function setSuperUserPermission() {
+ allUsersData.users[0] = superUserData;
+}
+
+function resetUserPermission() {
+ allUsersData.users[0] = {
+ ...allUsersData.users[0],
+ roles: { archived: false },
+ };
+}
+
describe('Discord Groups Page', () => {
let browser;
let page;
@@ -297,4 +308,66 @@ describe('Discord Groups Page', () => {
const repoLinkStyle = await page.evaluate((el) => el.style, repoLink);
expect(repoLinkStyle).toBeTruthy();
});
+
+ test('Should display delete button for super users', async () => {
+ setSuperUserPermission();
+ await page.goto(`${PAGE_URL}/groups?dev=true`);
+ await page.waitForNetworkIdle();
+ await page.waitForTimeout(1000);
+
+ const deleteButtons = await page.$$('.delete-group');
+ const cards = await page.$$('.card');
+ expect(deleteButtons.length).toBe(cards.length);
+ expect(deleteButtons.length).toBeGreaterThan(0);
+ });
+
+ test('Should not display delete button when user is normal user', async () => {
+ resetUserPermission();
+ await page.goto(`${PAGE_URL}/groups?dev=true`);
+ await page.waitForNetworkIdle();
+
+ const deleteButtons = await page.$$('.delete-group');
+ expect(deleteButtons.length).toBe(0);
+ });
+
+ test('Should not display delete button when dev=false', async () => {
+ setSuperUserPermission();
+ await page.goto(`${PAGE_URL}/groups`);
+ await page.waitForNetworkIdle();
+
+ const deleteButtons = await page.$$('.delete-group');
+ expect(deleteButtons.length).toBe(0);
+ });
+
+ test('Should display delete confirmation modal on click of delete button', async () => {
+ setSuperUserPermission();
+ await page.goto(`${PAGE_URL}/groups?dev=true`);
+ await page.waitForNetworkIdle();
+ await page.waitForTimeout(1000);
+
+ const deleteButton = await page.$('.delete-group');
+ await deleteButton.click();
+
+ const deleteConfirmationModal = await page.waitForSelector(
+ '.delete-confirmation-modal',
+ );
+
+ expect(deleteConfirmationModal).toBeTruthy();
+ });
+
+ test('Should close delete confirmation modal when cancel button is clicked', async () => {
+ setSuperUserPermission();
+ await page.goto(`${PAGE_URL}/groups?dev=true`);
+ await page.waitForNetworkIdle();
+ await page.waitForTimeout(1000);
+
+ const deleteButton = await page.$('.delete-group');
+ await deleteButton.click();
+
+ const cancelButton = await page.waitForSelector('#cancel-delete');
+ await cancelButton.click();
+
+ const modalClosed = await page.$('.delete-confirmation-modal');
+ expect(modalClosed).toBeFalsy();
+ });
});
diff --git a/__tests__/task-requests/task-requestDetails.test.js b/__tests__/task-requests/task-requestDetails.test.js
index 88be744e..0c827846 100644
--- a/__tests__/task-requests/task-requestDetails.test.js
+++ b/__tests__/task-requests/task-requestDetails.test.js
@@ -3,6 +3,63 @@ const {
urlMappings,
defaultMockResponseHeaders,
} = require('../../mock-data/taskRequests');
+const { user } = require('../../mock-data/users/index.js');
+
+describe('Request container for non-super users', () => {
+ let browser;
+ let page;
+ jest.setTimeout(60000);
+
+ beforeAll(async () => {
+ browser = await puppeteer.launch({
+ headless: 'new',
+ ignoreHTTPSErrors: true,
+ args: ['--incognito', '--disable-web-security'],
+ devtools: false,
+ });
+ page = await browser.newPage();
+ await page.setRequestInterception(true);
+ page.on('request', (interceptedRequest) => {
+ const url = interceptedRequest.url();
+ if (url == 'https://staging-api.realdevsquad.com/users/self') {
+ interceptedRequest.respond({
+ ...defaultMockResponseHeaders,
+ body: JSON.stringify(user),
+ });
+ } else if (urlMappings.hasOwnProperty(url)) {
+ interceptedRequest.respond({
+ ...defaultMockResponseHeaders,
+ body: JSON.stringify(urlMappings[url]),
+ });
+ } else {
+ interceptedRequest.continue();
+ }
+ });
+ await page.goto(
+ 'http://localhost:8000/task-requests/details/?id=dM5wwD9QsiTzi7eG7Oq5&dev=true',
+ );
+ });
+
+ afterAll(async () => {
+ await browser.close();
+ });
+
+ it('Approve and Reject buttons should not render for non-super users', async function () {
+ await page.waitForNetworkIdle();
+ const approveButton = await page.$('[data-testid="task-approve-button"]');
+ const rejectButton = await page.$('[data-testid="task-reject-button"]');
+ expect(approveButton).toBeNull();
+ expect(rejectButton).toBeNull();
+ });
+
+ it('Should render task status for non-super users', async function () {
+ await page.waitForNetworkIdle();
+ const taskRequestStatus = await page.$(
+ '[data-testid="requestors-task-status"]',
+ );
+ expect(taskRequestStatus).toBeTruthy();
+ });
+});
describe('Task request details page', () => {
let browser;
@@ -89,9 +146,13 @@ describe('Task request details page', () => {
);
});
- it('Should contain Approve and Reject buttons', async function () {
- const approveButton = await page.$('.requestors__conatainer__list__button');
- const rejectButton = await page.$('.request-details__reject__button');
+ it('Should render Approve and Reject buttons for super users', async function () {
+ await page.goto(
+ 'http://localhost:8000/task-requests/details/?id=dM5wwD9QsiTzi7eG7Oq5&dev=true',
+ );
+ await page.waitForNetworkIdle();
+ const approveButton = await page.$('[data-testid="task-approve-button"]');
+ const rejectButton = await page.$('[data-testid="task-reject-button"]');
expect(approveButton).toBeTruthy();
expect(rejectButton).toBeTruthy();
});
@@ -180,9 +241,13 @@ describe('Task request details page with markdown support in description', () =>
expect(descriptionHtmlValue).toContain('
Heading
');
});
- it('Should contain Approve and Reject buttons', async function () {
- const approveButton = await page.$('.requestors__conatainer__list__button');
- const rejectButton = await page.$('.request-details__reject__button');
+ it('Should render Approve and Reject buttons for super users', async function () {
+ await page.goto(
+ 'http://localhost:8000/task-requests/details/?id=dM5wwD9QsiTzi7eG7Oq6&dev=true',
+ );
+ await page.waitForNetworkIdle();
+ const approveButton = await page.$('[data-testid="task-approve-button"]');
+ const rejectButton = await page.$('[data-testid="task-reject-button"]');
expect(approveButton).toBeTruthy();
expect(rejectButton).toBeTruthy();
});
diff --git a/__tests__/users/App.test.js b/__tests__/users/App.test.js
index 05b36b53..f501677c 100644
--- a/__tests__/users/App.test.js
+++ b/__tests__/users/App.test.js
@@ -37,9 +37,13 @@ describe('App Component', () => {
headers,
body: JSON.stringify({
...filteredUsersData,
- users: filteredUsersData.users.filter(
- (user) => user.roles.in_discord,
- ),
+ ...mockUserData,
+ users: [
+ ...filteredUsersData.users.filter(
+ (user) => user.roles.in_discord,
+ ),
+ ...mockUserData.users.filter((user) => user.roles.in_discord),
+ ],
}),
});
} else if (url === `${API_BASE_URL}/users/search/?verified=true`) {
@@ -49,9 +53,11 @@ describe('App Component', () => {
headers,
body: JSON.stringify({
...filteredUsersData,
- users: filteredUsersData.users.filter((user) => user.discordId),
...mockUserData,
- users: mockUserData.users.filter((user) => user.discordId),
+ users: [
+ ...filteredUsersData.users.filter((user) => user.discordId),
+ ...mockUserData.users.filter((user) => user.discordId),
+ ],
}),
});
} else {
@@ -87,13 +93,55 @@ describe('App Component', () => {
});
it('should update the URL query string and re-render the app', async () => {
- await page.waitForSelector('[data_key="verified"]');
-
- // Click on the "Linked Accounts" tab
- await page.click('[data_key="verified"]');
+ await page.waitForSelector('[data-testid="tabs-section-select"]');
+ await page.select('[data-testid="tabs-section-select"]', 'verified');
// Get the current URL and make sure the query string has been updated
const url = await page.url();
expect(url).toContain('?tab=verified');
});
+
+ it('should handle user card clicks and apply active_tab class to clicked card only in discord tab', async () => {
+ await page.goto(`${BASE_URL}/users/discord/?tab=in_discord`);
+ await page.waitForNetworkIdle();
+ await page.waitForSelector('.user_card[data-key]');
+ const userCardTestIds = await page.$$eval(
+ '[data-testid^="user-card-"]',
+ (cards) => cards.map((card) => card.getAttribute('data-testid')),
+ );
+ for (let i = 0; i < userCardTestIds.length; i++) {
+ const userCardSelector = `[data-testid="${userCardTestIds[i]}"]`;
+ const userCardElement = await page.$(userCardSelector);
+ await userCardElement.click();
+ await page.waitForTimeout(1000);
+ const isActive = await page.evaluate((selector) => {
+ return document
+ .querySelector(selector)
+ ?.classList.contains('active_tab');
+ }, userCardSelector);
+ expect(isActive).toBe(true);
+ }
+ });
+
+ it('should handle user card clicks and apply active_tab class to clicked card only verified tab', async () => {
+ await page.goto(`${BASE_URL}/users/discord/?tab=verified`);
+ await page.waitForNetworkIdle();
+ await page.waitForSelector('.user_card[data-key]');
+ const userCardTestIds = await page.$$eval(
+ '[data-testid^="user-card-"]',
+ (cards) => cards.map((card) => card.getAttribute('data-testid')),
+ );
+ for (let i = 0; i < userCardTestIds.length; i++) {
+ const userCardSelector = `[data-testid="${userCardTestIds[i]}"]`;
+ const userCardElement = await page.$(userCardSelector);
+ await userCardElement.click();
+ await page.waitForTimeout(1000);
+ const isActive = await page.evaluate((selector) => {
+ return document
+ .querySelector(selector)
+ ?.classList.contains('active_tab');
+ }, userCardSelector);
+ expect(isActive).toBe(true);
+ }
+ });
});
diff --git a/__tests__/users/applyFilterPagination.test.js b/__tests__/users/applyFilterPagination.test.js
index a987fa99..e9561f0e 100644
--- a/__tests__/users/applyFilterPagination.test.js
+++ b/__tests__/users/applyFilterPagination.test.js
@@ -78,8 +78,8 @@ describe('Apply Filter and Pagination Functionality', () => {
});
it('should update the URL query string when applying filters', async () => {
- // click on the "Verified" tab
- await page.click('[data_key="verified"]');
+ await page.waitForSelector('[data-testid="tabs-section-select"]');
+ await page.select('[data-testid="tabs-section-select"]', 'verified');
// get the current URL
const url = await page.url();
diff --git a/__tests__/users/user-management-home-screen.test.js b/__tests__/users/user-management-home-screen.test.js
index 66f7b60c..f50242eb 100644
--- a/__tests__/users/user-management-home-screen.test.js
+++ b/__tests__/users/user-management-home-screen.test.js
@@ -9,9 +9,6 @@ describe('Tests the User Management User Listing Screen', () => {
let tileViewBtn;
let tableViewBtn;
let userSearchElement;
- let paginationElement;
- let prevBtn;
- let nextBtn;
jest.setTimeout(60000);
beforeAll(async () => {
@@ -71,9 +68,6 @@ describe('Tests the User Management User Listing Screen', () => {
tileViewBtn = await page.$('#tile-view-btn');
tableViewBtn = await page.$('#table-view-btn');
userSearchElement = await page.$('#user-search');
- paginationElement = await page.$('#pagination');
- prevBtn = await page.$('#prevButton');
- nextBtn = await page.$('#nextButton');
});
afterAll(async () => {
@@ -85,9 +79,6 @@ describe('Tests the User Management User Listing Screen', () => {
expect(tileViewBtn).toBeTruthy();
expect(tableViewBtn).toBeTruthy();
expect(userSearchElement).toBeTruthy();
- expect(paginationElement).toBeTruthy();
- expect(prevBtn).toBeTruthy();
- expect(nextBtn).toBeTruthy();
});
it('Check the UI interactions of tile view and table view button.', async () => {
diff --git a/extension-requests/script.js b/extension-requests/script.js
index dfe1818b..19d574c8 100644
--- a/extension-requests/script.js
+++ b/extension-requests/script.js
@@ -623,26 +623,16 @@ async function createExtensionCard(data, dev) {
});
deadlineContainer.appendChild(deadlineText);
- let deadlineValue;
- if (dev) {
- deadlineValue = createElement({
- type: 'span',
- attributes: {
- class: 'skeleton-span',
- 'data-testid': 'skeleton-span',
- },
- });
- } else {
- deadlineValue = createElement({
- type: 'span',
- innerText: `${deadlineDays}`,
- attributes: {
- class: `tooltip-container ${
- isDeadLineCrossed && isStatusPending ? 'red-text' : ''
- }`,
- },
- });
- }
+ const deadlineValue = createElement({
+ type: 'span',
+ innerText: `${deadlineDays}`,
+ attributes: {
+ class: `tooltip-container ${
+ isDeadLineCrossed && isStatusPending ? 'red-text' : ''
+ }`,
+ },
+ });
+
deadlineContainer.appendChild(deadlineValue);
const deadlineTooltip = createElement({
type: 'span',
@@ -662,24 +652,14 @@ async function createExtensionCard(data, dev) {
});
requestedContainer.appendChild(requestedText);
- let requestedValue;
- if (dev) {
- requestedValue = createElement({
- type: 'span',
- attributes: {
- class: 'skeleton-text',
- 'data-testid': 'skeleton-text',
- },
- });
- } else {
- requestedValue = createElement({
- type: 'span',
- attributes: {
- class: `requested-day tooltip-container ${requestedDaysTextColor}`,
- },
- innerText: `${requestedDaysAgo}`,
- });
- }
+ const requestedValue = createElement({
+ type: 'span',
+ attributes: {
+ class: `requested-day tooltip-container ${requestedDaysTextColor}`,
+ },
+ innerText: ` ${requestedDaysAgo}`,
+ });
+
const requestedToolTip = createElement({
type: 'span',
attributes: { class: 'tooltip' },
@@ -744,19 +724,12 @@ async function createExtensionCard(data, dev) {
});
newDeadlineContainer.appendChild(newDeadlineText);
- let newDeadlineValue;
- if (dev) {
- newDeadlineValue = createElement({
- type: 'span',
- attributes: { class: 'skeleton-span', 'data-testid': 'skeleton-span' },
- });
- } else {
- newDeadlineValue = createElement({
- type: 'span',
- attributes: { class: 'requested-day tooltip-container' },
- innerText: ` ${newDeadlineDays}`,
- });
- }
+ const newDeadlineValue = createElement({
+ type: 'span',
+ attributes: { class: 'requested-day tooltip-container' },
+ innerText: ` ${newDeadlineDays}`,
+ });
+
const newDeadlineToolTip = createElement({
type: 'span',
attributes: { class: 'tooltip' },
@@ -776,19 +749,12 @@ async function createExtensionCard(data, dev) {
});
extensionForContainer.appendChild(extensionForText);
- let extensionForValue;
- if (dev) {
- extensionForValue = createElement({
- type: 'span',
- attributes: { class: 'skeleton-span' },
- });
- } else {
- extensionForValue = createElement({
- type: 'span',
- attributes: { class: 'tooltip-container' },
- innerText: ` +${extensionDays}`,
- });
- }
+ const extensionForValue = createElement({
+ type: 'span',
+ attributes: { class: 'tooltip-container' },
+ innerText: ` +${extensionDays}`,
+ });
+
const extensionToolTip = createElement({
type: 'span',
attributes: { class: 'tooltip' },
@@ -821,19 +787,12 @@ async function createExtensionCard(data, dev) {
const requestNumber = data.requestNumber || 1;
- let extensionRequestNumberValue;
- if (dev) {
- extensionRequestNumberValue = createElement({
- type: 'span',
- attributes: { class: 'skeleton-span', 'data-testid': 'skeleton-span' },
- });
- } else {
- extensionRequestNumberValue = createElement({
- type: 'span',
- attributes: { class: 'extension-request-number' },
- innerText: `#${requestNumber}`,
- });
- }
+ const extensionRequestNumberValue = createElement({
+ type: 'span',
+ attributes: { class: 'extension-request-number' },
+ innerText: `#${requestNumber}`,
+ });
+
extensionRequestNumberContainer.appendChild(extensionRequestNumberValue);
const cardAssigneeButtonContainer = createElement({
type: 'div',
@@ -1330,44 +1289,7 @@ async function createExtensionCard(data, dev) {
CommitedHoursContent.innerText = 'Missing';
CommitedHoursContent.classList.add('label-content-missing');
}
- if (dev) {
- deadlineValue.classList.remove('skeleton-span');
- deadlineValue.innerText = `${deadlineDays}`;
-
- deadlineValue.classList.add('tooltip-container');
- if (isDeadLineCrossed && isStatusPending) {
- deadlineValue.classList.add('red-text');
- }
- }
-
- if (dev) {
- requestedValue.classList.remove('skeleton-text');
- requestedValue.innerText = `${requestedDaysAgo}`;
-
- requestedValue.classList.add(
- 'requested-day',
- 'tooltip-container',
- requestedDaysTextColor,
- );
- }
-
- if (dev) {
- newDeadlineValue.classList.remove('skeleton-span');
- newDeadlineValue.innerText = ` ${newDeadlineDays}`;
- newDeadlineValue.classList.add('requested-day', 'tooltip-container');
- }
- if (dev) {
- extensionForValue.classList.remove('skeleton-span');
- extensionForValue.innerText = ` +${extensionDays}`;
- extensionForValue.classList.add('tooltip-container');
- }
-
- if (dev) {
- extensionRequestNumberValue.classList.remove('skeleton-span');
- extensionRequestNumberValue.innerText = `#${requestNumber}`;
- extensionRequestNumberValue.classList.add('extension-request-number');
- }
if (!dev) {
removeSpinner();
renderExtensionCreatedLog();
diff --git a/groups/assets/delete.svg b/groups/assets/delete.svg
new file mode 100644
index 00000000..1ca5a7a1
--- /dev/null
+++ b/groups/assets/delete.svg
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/groups/createElements.js b/groups/createElements.js
index 44254e8a..3a7c0270 100644
--- a/groups/createElements.js
+++ b/groups/createElements.js
@@ -1,4 +1,9 @@
-const createCard = (rawGroup, onClick = () => {}) => {
+const createCard = (
+ rawGroup,
+ onClick = () => {},
+ onDelete = () => {},
+ isSuperUser = false,
+) => {
const group = {
...rawGroup,
description:
@@ -10,7 +15,17 @@ const createCard = (rawGroup, onClick = () => {}) => {
cardElement.className = 'card';
cardElement.id = `group-${group.id}`;
cardElement.innerHTML = `
-
+
@@ -36,6 +51,15 @@ const createCard = (rawGroup, onClick = () => {}) => {
.querySelector('.card__btn')
.addEventListener('click', () => group.isUpdating || onClick());
+ if (isSuperUser) {
+ cardElement
+ .querySelector('.delete-group')
+ .addEventListener('click', (e) => {
+ e.stopPropagation();
+ onDelete(rawGroup.id, rawGroup.roleId);
+ });
+ }
+
return cardElement;
};
@@ -214,6 +238,44 @@ const createGroupCreationModal = (onClose = () => {}, onSubmit = () => {}) => {
return backdropElement;
};
+const createDeleteConfirmationModal = (
+ onClose = () => {},
+ onConfirm = () => {},
+) => {
+ const backdropElement = document.createElement('div');
+ backdropElement.className = 'backdrop';
+
+ const modalElement = document.createElement('div');
+ modalElement.className = 'delete-confirmation-modal';
+ modalElement.innerHTML = `
+
+
+
Are you sure you want to delete this group?
+
+
+
+
+
+
+ `;
+
+ modalElement.querySelector('#close-button').onclick = onClose;
+ modalElement.querySelector('#cancel-delete').onclick = onClose;
+ modalElement.querySelector('#confirm-delete').onclick = onConfirm;
+
+ backdropElement.appendChild(modalElement);
+ backdropElement.onclick = (e) => {
+ if (e.target === backdropElement) onClose();
+ };
+
+ return backdropElement;
+};
+
export {
createCard,
createLoadingCard,
@@ -222,4 +284,5 @@ export {
createNavbarProfileLoading,
createNavbarProfileSignin,
createGroupCreationModal,
+ createDeleteConfirmationModal,
};
diff --git a/groups/index.html b/groups/index.html
index b58fcdcf..a2e188ab 100644
--- a/groups/index.html
+++ b/groups/index.html
@@ -10,6 +10,7 @@
+
diff --git a/groups/render.js b/groups/render.js
index c8513b2a..9647b07d 100644
--- a/groups/render.js
+++ b/groups/render.js
@@ -6,6 +6,7 @@ import {
createNavbarProfile,
createNavbarProfileLoading,
createNavbarProfileSignin,
+ createDeleteConfirmationModal,
} from './createElements.js';
const renderNotAuthenticatedPage = () => {
@@ -86,8 +87,13 @@ const removeLoadingCards = () => {
loadingCards.forEach((card) => mainContainer.removeChild(card));
};
-const renderGroupById = ({ group, cardOnClick = () => {} }) => {
- const card = createCard(group, cardOnClick);
+const renderGroupById = ({
+ group,
+ cardOnClick = () => {},
+ onDelete = () => {},
+ isSuperUser = false,
+}) => {
+ const card = createCard(group, cardOnClick, onDelete, isSuperUser);
const mainContainer = document.querySelector('.group-container');
const groupElement = document.getElementById(`group-${group.id}`);
if (groupElement) {
@@ -105,6 +111,29 @@ const renderNoGroupFound = () => {
mainContainer.append(noGroupContainer);
};
+const renderDeleteConfirmationModal = ({
+ onClose = () => {},
+ onConfirm = () => {},
+}) => {
+ const container = document.querySelector('body');
+ const existingBackdrop = document.querySelector('.backdrop');
+
+ if (existingBackdrop) {
+ container.removeChild(existingBackdrop);
+ }
+
+ const modal = createDeleteConfirmationModal(onClose, onConfirm);
+ container.appendChild(modal);
+};
+
+const removeDeleteConfirmationModal = () => {
+ const container = document.querySelector('body');
+ const backdrop = document.querySelector('.backdrop');
+ if (backdrop) {
+ container.removeChild(backdrop);
+ }
+};
+
export {
renderNotAuthenticatedPage,
renderGroupCreationModal,
@@ -117,4 +146,6 @@ export {
removeLoadingCards,
renderGroupById,
renderNoGroupFound,
+ renderDeleteConfirmationModal,
+ removeDeleteConfirmationModal,
};
diff --git a/groups/script.js b/groups/script.js
index 9ac12657..7b42e819 100644
--- a/groups/script.js
+++ b/groups/script.js
@@ -11,7 +11,10 @@ import {
renderNavbarProfile,
renderNavbarProfileSignin,
renderNotAuthenticatedPage,
+ renderDeleteConfirmationModal,
+ removeDeleteConfirmationModal,
} from './render.js';
+
import {
addGroupRoleToMember,
createDiscordGroupRole,
@@ -22,11 +25,14 @@ import {
getDiscordGroupIdsFromSearch,
getParamValueFromURL,
setParamValueInURL,
+ deleteDiscordGroupRole,
} from './utils.js';
const QUERY_PARAM_KEY = {
+ DEV_FEATURE_FLAG: 'dev',
GROUP_SEARCH: 'name',
};
+const isDev = getParamValueFromURL(QUERY_PARAM_KEY.DEV_FEATURE_FLAG) === 'true';
const handler = {
set: (obj, prop, value) => {
@@ -42,11 +48,16 @@ const handler = {
.filter(
(ng) => JSON.stringify(oldGroups?.[ng.id]) !== JSON.stringify(ng),
)
- .filter((ng) => dataStore.filteredGroupsIds.includes(ng.id));
+ .filter((ng) => dataStore.filteredGroupsIds.includes(ng.id))
+ .filter((ng) => !ng.isDeleted);
diffGroups.forEach((group) =>
renderGroupById({
- group,
+ group: {
+ ...dataStore.groups[group.id],
+ roleId: dataStore.groups[group.id].roleid,
+ },
cardOnClick: () => groupCardOnAction(group.id),
+ isSuperUser: dataStore.isSuperUser,
}),
);
break;
@@ -99,6 +110,9 @@ const handler = {
case 'discordId':
obj[prop] = value;
break;
+ case 'isSuperUser':
+ obj[prop] = value;
+ break;
default:
throw new Error('Invalid property set');
}
@@ -114,6 +128,7 @@ const dataStore = new Proxy(
search: getParamValueFromURL(QUERY_PARAM_KEY.GROUP_SEARCH),
discordId: null,
isCreateGroupModalOpen: false,
+ isSuperUser: false,
},
handler,
);
@@ -155,10 +170,13 @@ const onCreate = () => {
};
const afterAuthentication = async () => {
renderNavbarProfile({ profile: dataStore.userSelf });
+ dataStore.isSuperUser = await checkUserIsSuperUser();
+
await Promise.all([getDiscordGroups(), getUserGroupRoles()]).then(
([groups, roleData]) => {
- dataStore.filteredGroupsIds = groups.map((group) => group.id);
- dataStore.groups = groups.reduce((acc, group) => {
+ const nonDeletedGroups = groups.filter((group) => !group.isDeleted);
+ dataStore.filteredGroupsIds = nonDeletedGroups.map((group) => group.id);
+ dataStore.groups = nonDeletedGroups.reduce((acc, group) => {
let title = group.rolename
.replace('group-', '')
.split('-')
@@ -180,6 +198,9 @@ const afterAuthentication = async () => {
dataStore.search,
);
dataStore.discordId = roleData.userId;
+ renderAllGroups({
+ cardOnClick: groupCardOnAction,
+ });
},
);
};
@@ -254,12 +275,52 @@ function groupCardOnAction(id) {
function renderAllGroups({ cardOnClick }) {
const mainContainer = document.querySelector('.group-container');
mainContainer.innerHTML = '';
- dataStore.filteredGroupsIds.forEach((id) =>
- renderGroupById({
- group: dataStore.groups[id],
- cardOnClick: () => cardOnClick(id),
- }),
+ const nonDeletedGroups = dataStore.filteredGroupsIds.filter(
+ (id) => !dataStore.groups[id].isDeleted,
);
+ if (nonDeletedGroups.length === 0) {
+ renderNoGroupFound();
+ } else {
+ nonDeletedGroups.forEach((id) => {
+ const group = dataStore.groups[id];
+ if (!group.isDeleted) {
+ renderGroupById({
+ group: group,
+ cardOnClick: () => cardOnClick(id),
+ onDelete: isDev ? showDeleteModal : undefined,
+ isSuperUser: dataStore.isSuperUser && isDev,
+ });
+ }
+ });
+ }
+}
+
+function showDeleteModal(groupId, roleId) {
+ if (!isDev) return;
+ renderDeleteConfirmationModal({
+ onClose: () => {
+ removeDeleteConfirmationModal();
+ },
+ onConfirm: async () => {
+ try {
+ await deleteDiscordGroupRole(groupId, roleId);
+ showToaster('Group deleted successfully');
+
+ updateGroup(groupId, { isDeleted: true });
+
+ dataStore.filteredGroupsIds = dataStore.filteredGroupsIds.filter(
+ (id) => id !== groupId,
+ );
+ renderAllGroups({
+ cardOnClick: groupCardOnAction,
+ });
+ } catch (error) {
+ showToaster(error.message || 'Failed to delete group');
+ } finally {
+ removeDeleteConfirmationModal();
+ }
+ },
+ });
}
onCreate();
diff --git a/groups/style.css b/groups/style.css
index 054e47b4..b9b966f8 100644
--- a/groups/style.css
+++ b/groups/style.css
@@ -127,6 +127,12 @@ body {
grid-template-columns: repeat(4, 1fr);
}
+@media screen and (max-width: 400px) {
+ .group-header {
+ gap: 0.8rem;
+ }
+}
+
.spacer {
flex-grow: 1;
}
@@ -345,6 +351,32 @@ body {
width: 100%;
}
+.card__header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.delete-group {
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+}
+
+.delete-group__icon {
+ height: 1.2em;
+ opacity: 0.5;
+ transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out,
+ scale 0.2s ease-in-out;
+}
+
+.delete-group:hover .delete-group__icon {
+ transform: rotate(10deg);
+ opacity: 1;
+ scale: 1.2;
+}
+
.card__title {
font-size: 1.1rem;
font-weight: 600;
@@ -592,3 +624,78 @@ body {
gap: 0;
}
}
+
+/*
+For Delete Confirmation Modal
+*/
+
+.delete-confirmation-modal {
+ background-color: var(--color-white);
+ border-radius: 8px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ max-width: 400px;
+ width: 90%;
+ padding: 24px;
+ position: relative;
+}
+
+.delete-modal__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+}
+
+.delete-modal__title {
+ font-size: 24px;
+ font-weight: bold;
+ color: #333;
+ margin: 0;
+}
+
+.delete-modal__close {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0.8rem 1rem;
+ margin: -1rem -0.8rem 0.8rem 0.4rem;
+}
+
+.delete-modal__content {
+ margin-bottom: 24px;
+}
+
+.delete-modal__msg {
+ font-size: 16px;
+ color: #666;
+ line-height: 1.5;
+}
+
+.delete-modal__buttons {
+ display: flex;
+ justify-content: flex-end;
+ gap: 12px;
+}
+
+.delete-modal-button {
+ padding: 10px 20px;
+ border-radius: 4px;
+ font-size: 16px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+
+.button--secondary:hover {
+ background-color: var(--color-group-btn-background);
+}
+
+.button--danger {
+ background-color: #dc3545;
+ color: #fff;
+ border: none;
+}
+
+.button--danger:hover {
+ background-color: #c82333;
+}
diff --git a/groups/utils.js b/groups/utils.js
index 4dfeff83..e6437a0e 100644
--- a/groups/utils.js
+++ b/groups/utils.js
@@ -1,5 +1,4 @@
const BASE_URL = window.API_BASE_URL; // REPLACE WITH YOUR LOCALHOST URL FOR TESTING LOCAL BACKEND
-
async function getMembers() {
try {
const res = await fetch(`${BASE_URL}/users/`, {
@@ -117,6 +116,33 @@ async function removeRoleFromMember(roleId, discordId) {
}
}
+async function deleteDiscordGroupRole(groupId, roleId) {
+ try {
+ const res = await fetch(
+ `${BASE_URL}/discord-actions/groups/${groupId}?dev=true`,
+ {
+ method: 'DELETE',
+ credentials: 'include',
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ body: JSON.stringify({ roleid: roleId }),
+ },
+ );
+
+ if (!res.ok) {
+ const errorResponse = await res.json();
+ throw new Error(
+ `Failed to delete group role: ${JSON.stringify(errorResponse.error)}`,
+ );
+ }
+
+ return await res.json();
+ } catch (err) {
+ throw err;
+ }
+}
+
function removeGroupKeywordFromDiscordRoleName(groupName) {
if (/^group.*/.test(groupName)) {
const splitNames = groupName.split('-');
@@ -164,6 +190,7 @@ export {
createDiscordGroupRole,
addGroupRoleToMember,
removeRoleFromMember,
+ deleteDiscordGroupRole,
removeGroupKeywordFromDiscordRoleName,
getDiscordGroupIdsFromSearch,
getParamValueFromURL,
diff --git a/mock-data/taskRequests/index.js b/mock-data/taskRequests/index.js
index 3b5d184d..c17e92ca 100644
--- a/mock-data/taskRequests/index.js
+++ b/mock-data/taskRequests/index.js
@@ -1,3 +1,5 @@
+const { superUserDetails } = require('../users/mockdata.js');
+
const fetchedTaskRequests = [
{
id: '123CCXSDF123',
@@ -260,7 +262,6 @@ const githubIssue = {
performed_via_github_app: null,
state_reason: 'completed',
};
-
const individualTaskDetail = {
message: 'task returned successfully',
taskData: {
@@ -306,7 +307,6 @@ const userInformationTaskCreation = {
},
},
};
-
const userInformation = {
message: 'User returned successfully!',
user: {
@@ -351,6 +351,7 @@ const defaultMockResponseHeaders = {
};
const urlMappings = {
+ 'https://staging-api.realdevsquad.com/users/self': superUserDetails.user,
'https://api.realdevsquad.com/taskRequests/dM5wwD9QsiTzi7eG7Oq5':
individualTaskReqDetail,
'https://api.realdevsquad.com/taskRequests/dM5wwD9QsiTzi7eG7Oq6':
diff --git a/task-requests/details/index.html b/task-requests/details/index.html
index 5dc02fbb..7543e635 100644
--- a/task-requests/details/index.html
+++ b/task-requests/details/index.html
@@ -24,13 +24,13 @@
crossorigin="anonymous"
referrerpolicy="no-referrer"
>
-
+
-
-
-
diff --git a/task-requests/details/script.js b/task-requests/details/script.js
index acab0c50..757c0697 100644
--- a/task-requests/details/script.js
+++ b/task-requests/details/script.js
@@ -2,8 +2,9 @@ const API_BASE_URL =
window.location.hostname === 'localhost'
? 'https://staging-api.realdevsquad.com'
: window.API_BASE_URL;
-
+import { getIsSuperUser } from '../../applications/utils.js';
let taskRequest;
+let isSuperUser;
const taskRequestSkeleton = document.querySelector('.taskRequest__skeleton');
const container = document.querySelector('.container');
@@ -11,17 +12,18 @@ const taskSkeleton = document.querySelector('.task__skeleton');
const requestorSkeleton = document.querySelector(
'.requestors__container__list__skeleton',
);
-
const taskRequestContainer = document.getElementById('task-request-details');
+const requestDetailContainer =
+ document.getElementsByClassName('request-details');
const taskContainer = document.getElementById('task-details');
const toast = document.getElementById('toast_task_details');
-const rejectButton = document.getElementById('reject-button');
const requestorsContainer = document.getElementById('requestors-details');
const taskRequestId = new URLSearchParams(window.location.search).get('id');
history.pushState({}, '', window.location.href);
const errorMessage =
'The requested operation could not be completed. Please try again later.';
-let taskId;
+const params = new URLSearchParams(window.location.search);
+const isDev = params.get('dev') === 'true';
function renderTaskRequestDetails(taskRequest) {
taskRequestContainer.append(
@@ -200,30 +202,65 @@ async function updateTaskRequest(action, userId) {
}
}
-function getActionButton(requestor) {
- if (taskRequest?.status === taskRequestStatus.APPROVED) {
- if (taskRequest.approvedTo === requestor?.user?.id) {
+function renderActionButton(requestor, taskRequest) {
+ if (isDev) {
+ if (isSuperUser) {
+ if (taskRequest?.status === taskRequestStatus.APPROVED) {
+ return taskRequest.approvedTo === requestor?.user?.id
+ ? createCustomElement({
+ tagName: 'p',
+ textContent: 'Approved',
+ class: ['requestors__container__list__status'],
+ })
+ : '';
+ }
return createCustomElement({
- tagName: 'p',
- textContent: 'Approved',
- class: ['requestors__container__list__approved'],
+ tagName: 'button',
+ textContent: 'Approve',
+ class: 'requestors__conatainer__list__button',
+ 'data-testid': 'task-approve-button',
+ eventListeners: [
+ {
+ event: 'click',
+ func: () =>
+ updateTaskRequest(TaskRequestAction.APPROVE, requestor.user?.id),
+ },
+ ],
});
- } else {
- return '';
}
+ return createCustomElement({
+ tagName: 'p',
+ textContent:
+ taskRequest.status[0].toUpperCase() +
+ taskRequest.status.slice(1).toLowerCase(),
+ class: ['requestors__container__list__status'],
+ 'data-testid': 'requestors-task-status',
+ });
+ } else {
+ if (taskRequest?.status === taskRequestStatus.APPROVED) {
+ if (taskRequest.approvedTo === requestor?.user?.id) {
+ return createCustomElement({
+ tagName: 'p',
+ textContent: 'Approved',
+ class: ['requestors__container__list__status'],
+ });
+ } else {
+ return '';
+ }
+ }
+ return createCustomElement({
+ tagName: 'button',
+ textContent: 'Approve',
+ class: 'requestors__conatainer__list__button',
+ eventListeners: [
+ {
+ event: 'click',
+ func: () =>
+ updateTaskRequest(TaskRequestAction.APPROVE, requestor.user?.id),
+ },
+ ],
+ });
}
- return createCustomElement({
- tagName: 'button',
- textContent: 'Approve',
- class: 'requestors__conatainer__list__button',
- eventListeners: [
- {
- event: 'click',
- func: () =>
- updateTaskRequest(TaskRequestAction.APPROVE, requestor.user?.id),
- },
- ],
- });
}
async function renderRequestors(taskRequest) {
@@ -279,14 +316,22 @@ async function renderRequestors(taskRequest) {
createCustomElement({
tagName: 'div',
child: [
- taskRequest.status !== 'DENIED' ? getActionButton(requestor) : '',
+ isDev
+ ? taskRequest.status !== 'DENIED'
+ ? renderActionButton(requestor, taskRequest)
+ : createCustomElement({
+ tagName: 'p',
+ textContent: 'Denied',
+ class: ['requestors__container__list__status'],
+ 'data-testid': 'requestor-container-task-status',
+ })
+ : taskRequest.status !== 'DENIED'
+ ? renderActionButton(requestor, taskRequest)
+ : '',
],
}),
],
});
- const avatarDiv = userDetailsDiv.querySelector(
- '.requestors__container__list__userDetails__avatar',
- );
requestorsContainer.append(userDetailsDiv);
});
}
@@ -305,7 +350,7 @@ async function fetchTaskRequest() {
}
const renderGithubIssue = async () => {
- converter = new showdown.Converter({
+ const converter = new showdown.Converter({
tables: true,
simplifiedAutoLink: true,
tasklists: true,
@@ -344,7 +389,7 @@ const renderGithubIssue = async () => {
}),
);
const body = DOMPurify.sanitize(res?.body ?? '');
- html = converter.makeHtml(body);
+ const html = converter.makeHtml(body);
taskContainer.appendChild(
createCustomElement({
tagName: 'div',
@@ -410,22 +455,82 @@ const renderGithubIssue = async () => {
);
};
const renderRejectButton = (taskRequest) => {
- if (taskRequest?.status !== 'PENDING') {
- rejectButton.disabled = true;
- }
+ if (isDev) {
+ if (!isSuperUser) return;
+ if (taskRequest?.status === 'PENDING') {
+ const rejectContainer = createCustomElement({
+ tagName: 'div',
+ class: 'reject__container',
+ child: [
+ createCustomElement({
+ tagName: 'button',
+ textContent: 'Reject',
+ id: 'reject-button',
+ class: 'request-details__reject__button',
+ 'data-testid': 'task-reject-button',
+ }),
+ ],
+ });
+
+ requestDetailContainer[0].appendChild(rejectContainer);
+ const rejectButton = rejectContainer.querySelector('#reject-button');
+
+ rejectButton.addEventListener('click', async () => {
+ const res = await updateTaskRequest(TaskRequestAction.REJECT);
+ if (res?.ok) {
+ rejectButton.remove();
+ }
+ });
+ } else {
+ const existingRejectContainer =
+ document.querySelector('.reject__container');
+ if (existingRejectContainer) {
+ const rejectButton =
+ existingRejectContainer.querySelector('#reject-button');
+ if (rejectButton) {
+ rejectButton.remove();
+ }
+ }
+ }
+ } else {
+ const rejectContainer = document.querySelector('.reject__container');
+ if (!rejectContainer) {
+ const rejectContainer = createCustomElement({
+ tagName: 'div',
+ class: 'reject__container',
+ child: [
+ createCustomElement({
+ tagName: 'button',
+ textContent: 'Reject',
+ id: 'reject-button',
+ class: 'request-details__reject__button',
+ 'data-testid': 'task-reject-button',
+ }),
+ ],
+ });
- rejectButton.addEventListener('click', async () => {
- const res = await updateTaskRequest(TaskRequestAction.REJECT);
- if (res?.ok) {
+ requestDetailContainer[0].appendChild(rejectContainer);
+ }
+ const rejectButton = document.querySelector('#reject-button');
+ if (taskRequest?.status !== 'PENDING') {
rejectButton.disabled = true;
}
- });
+
+ rejectButton.addEventListener('click', async () => {
+ const res = await updateTaskRequest(TaskRequestAction.REJECT);
+ if (res?.ok) {
+ rejectButton.disabled = true;
+ }
+ });
+ }
};
+
const renderTaskRequest = async () => {
taskRequestSkeleton.classList.remove('hidden');
taskContainer.classList.remove('hidden');
try {
taskRequest = await fetchTaskRequest();
+ isSuperUser = await getIsSuperUser();
taskRequestSkeleton.classList.add('hidden');
renderRejectButton(taskRequest);
renderTaskRequestDetails(taskRequest);
@@ -568,7 +673,7 @@ function populateModalContent(index) {
);
if (userData?.markdownEnabled ?? false) {
- converter = new showdown.Converter({
+ const converter = new showdown.Converter({
tables: true,
simplifiedAutoLink: true,
tasklists: true,
@@ -577,7 +682,7 @@ function populateModalContent(index) {
openLinksInNewWindow: true,
});
const sanitizedDescription = DOMPurify.sanitize(userData.description ?? '');
- html = converter.makeHtml(sanitizedDescription);
+ const html = converter.makeHtml(sanitizedDescription);
descriptionValue.innerHTML = html;
descriptionValue.className = 'requestor_description_details';
} else {
diff --git a/task-requests/details/style.css b/task-requests/details/style.css
index 6083751f..f5ddd3a2 100644
--- a/task-requests/details/style.css
+++ b/task-requests/details/style.css
@@ -248,7 +248,7 @@ body {
background: #19805e;
transition: 0.3s ease-in-out;
}
-.requestors__container__list__approved {
+.requestors__container__list__status {
background: transparent;
border: none;
color: #c3c3c3;
diff --git a/task-requests/util.js b/task-requests/util.js
index ad50c912..d13fb0b9 100644
--- a/task-requests/util.js
+++ b/task-requests/util.js
@@ -17,6 +17,8 @@ function createCustomElement(domObjectMap) {
}
} else if (key === 'child') {
el.append(...value);
+ } else if (key.startsWith('data-')) {
+ el.setAttribute(key, value);
} else {
el[key] = value;
}
diff --git a/users/constants.js b/users/constants.js
index 78d86831..bf003d1b 100644
--- a/users/constants.js
+++ b/users/constants.js
@@ -8,9 +8,6 @@ const TILE_VIEW_BTN = 'tile-view-btn';
const TABLE_VIEW_BTN = 'table-view-btn';
const USER_SEARCH_ELEMENT = 'user-search';
const DEFAULT_AVATAR = '/images/avatar.png';
-const PAGINATION_ELEMENT = 'pagination';
-const PREV_BUTTON = 'prevButton';
-const NEXT_BUTTON = 'nextButton';
const USER_FETCH_COUNT = 100;
const NONE = 'NONE';
const OOO = 'OOO';
diff --git a/users/discord/App.js b/users/discord/App.js
index 87fa873d..2fe6dc37 100644
--- a/users/discord/App.js
+++ b/users/discord/App.js
@@ -7,8 +7,8 @@ import { NoUserFound } from './components/NoUserFound.js';
const { createElement, rerender } = react;
const tabs = [
- { display_name: 'In Discord', id: 'in_discord' },
- { display_name: 'Linked Accounts', id: 'verified' },
+ { display_name: 'In Discord', id: 'in_discord', value: 'in_discord' },
+ { display_name: 'Linked Accounts', id: 'verified', value: 'verified' },
];
export const usersData = {
in_discord: null,
@@ -22,7 +22,7 @@ let showUser = 0;
usersData[activeTab] = await getUsers(activeTab);
const handleTabNavigation = async (e) => {
- const selectedTabId = e.target.getAttribute('data_key');
+ const selectedTabId = e.target.value;
if (selectedTabId) {
document.location.search = `tab=${selectedTabId}`;
@@ -37,16 +37,16 @@ const handleTabNavigation = async (e) => {
}
};
-const handleUserSelected = (e) => {
- const selectedUserId =
- e.target?.getAttribute('data_key') ||
- e.target.parentElement?.getAttribute('data_key');
-
- if (selectedUserId) {
- showUser = usersData[activeTab]?.findIndex(
- (user) => user.id === selectedUserId,
+const handleUserSelected = (userId) => {
+ if (userId) {
+ const selectedUserIndex = usersData[activeTab]?.findIndex(
+ (user) => user.id === userId,
);
- rerender(App(), window['root']);
+
+ if (selectedUserIndex !== -1) {
+ showUser = selectedUserIndex;
+ rerender(App(), window['root']);
+ }
}
};
diff --git a/users/discord/components/TabsSection.js b/users/discord/components/TabsSection.js
index 57dd4416..9e3ff12d 100644
--- a/users/discord/components/TabsSection.js
+++ b/users/discord/components/TabsSection.js
@@ -2,18 +2,20 @@ const { createElement } = react;
export const TabsSection = ({ tabs, activeTab, handleTabNavigation }) => {
return createElement(
- 'div',
+ 'select',
{
class: 'tabs_section',
- onclick: handleTabNavigation,
- 'data-testid': 'tabs-section',
+ onchange: handleTabNavigation,
+ 'data-testid': 'tabs-section-select',
},
tabs.map((tabItem) => {
return createElement(
- 'span',
+ 'option',
{
data_key: `${tabItem.id}`,
class: `tab ${activeTab === tabItem.id ? 'active_tab' : ''}`,
+ value: `${tabItem.value}`,
+ ...(activeTab === tabItem.id && { selected: 'true' }),
},
[tabItem.display_name],
);
diff --git a/users/discord/components/UsersSection.js b/users/discord/components/UsersSection.js
index 4d0ac94b..51773f79 100644
--- a/users/discord/components/UsersSection.js
+++ b/users/discord/components/UsersSection.js
@@ -5,7 +5,7 @@ export const UsersSection = ({ users, showUser, handleUserSelected }) => {
'aside',
{
class: 'users_section',
- onClick: handleUserSelected,
+
'data-testid': 'users-section',
},
users?.map((user) => {
@@ -17,6 +17,7 @@ export const UsersSection = ({ users, showUser, handleUserSelected }) => {
}`,
'data-testid': `user-card-${user.id}`,
'data-key': user.id,
+ onclick: () => handleUserSelected(user.id),
},
[
createElement('img', {
diff --git a/users/discord/style.css b/users/discord/style.css
index 23058547..c44c8f11 100644
--- a/users/discord/style.css
+++ b/users/discord/style.css
@@ -1,5 +1,6 @@
:root {
--light-gray: #d4d4d478;
+ --border-color-gray: #808080;
}
.header {
@@ -24,13 +25,16 @@ main {
}
.tabs_section {
- padding: 1rem 0;
+ padding: 1rem;
margin: 1rem;
grid-area: tab;
+ width: 18rem;
+ border: 2px solid var(--border-color-gray);
+ font-size: unset;
}
.tab {
- padding: 0.5rem;
+ padding: 1rem;
margin-inline-end: 0.5rem;
cursor: pointer;
}
diff --git a/users/discord/utils/react.js b/users/discord/utils/react.js
index faa43304..ed807527 100644
--- a/users/discord/utils/react.js
+++ b/users/discord/utils/react.js
@@ -8,7 +8,8 @@ const render = function (element, container) {
const component = document.createElement(element.tag);
element.props &&
Object.keys(element.props).forEach((prop) =>
- prop == 'onclick' || prop == 'onsubmit'
+ // based on requirements, any other type of event listner can be added here as 'prop'
+ prop == 'onclick' || prop == 'onsubmit' || prop == 'onchange'
? (component[prop] = element.props[prop])
: component.setAttribute(prop, element.props[prop]),
);
diff --git a/users/discord/utils/util.js b/users/discord/utils/util.js
index 83cd40e5..1937147e 100644
--- a/users/discord/utils/util.js
+++ b/users/discord/utils/util.js
@@ -1,3 +1,8 @@
+const API_BASE_URL =
+ window.location.hostname === 'localhost'
+ ? 'https://staging-api.realdevsquad.com'
+ : window.API_BASE_URL;
+
export const getUsers = async (tab) => {
let URL = {
in_discord: `${API_BASE_URL}/users/search/?role=in_discord`,
diff --git a/users/index.html b/users/index.html
index 5bae7582..b1e65323 100644
--- a/users/index.html
+++ b/users/index.html
@@ -72,10 +72,6 @@
Skills
-