From a56cf190e9070db56db5b64738d1ccaa7ca301fa Mon Sep 17 00:00:00 2001
From: Vinayak Goyal <73058928+ivinayakg@users.noreply.github.com>
Date: Thu, 11 Apr 2024 04:44:15 +0530
Subject: [PATCH 1/5] feat: photo verification dashboard done
---
constants.js | 1 +
index.html | 7 +
photo-verification-requests/index.html | 31 +++
photo-verification-requests/local-utils.js | 220 +++++++++++++++++++++
photo-verification-requests/script.js | 35 ++++
photo-verification-requests/style.css | 177 +++++++++++++++++
script.js | 4 +
7 files changed, 475 insertions(+)
create mode 100644 photo-verification-requests/index.html
create mode 100644 photo-verification-requests/local-utils.js
create mode 100644 photo-verification-requests/script.js
create mode 100644 photo-verification-requests/style.css
diff --git a/constants.js b/constants.js
index 2095f752..c9387fcd 100644
--- a/constants.js
+++ b/constants.js
@@ -27,6 +27,7 @@ const DISABLED = 'disabled';
const STATUS_BASE_URL_PROD = 'https://status.realdevsquad.com';
const STATUS_BASE_URL_STAGING = 'https://staging-status.realdevsquad.com';
const STATUS_BASE_URL = STATUS_BASE_URL_PROD;
+const PHOTO_VERIFICATION_REQUESTS_BUTTON = 'photo-verification-requests-button';
const dummyPicture = 'https://dashboard.realdevsquad.com/images/avatar.png';
const USER_MANAGEMENT_URL =
diff --git a/index.html b/index.html
index cf325060..1eab8553 100644
--- a/index.html
+++ b/index.html
@@ -138,6 +138,13 @@
>
Extension Requests
+
+ Photo Verification Requests
+
+
+
+
+
+
+ Photo Verification Requests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/photo-verification-requests/local-utils.js b/photo-verification-requests/local-utils.js
new file mode 100644
index 00000000..80413af5
--- /dev/null
+++ b/photo-verification-requests/local-utils.js
@@ -0,0 +1,220 @@
+async function approvePhotoVerificationRequest(id, userId, imageType) {
+ const res = await modifyPhotoVerificationRequest(
+ userId,
+ imageType,
+ 'APPROVED',
+ );
+ const card = document.querySelector(`.photo-verification-card--${id}`);
+ const message = document.createElement('p');
+ message.innerText = res.data.message;
+
+ card.appendChild(message);
+
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+}
+
+async function rejectPhotoVerificationRequest(id, userId) {
+ const res = await modifyPhotoVerificationRequest(userId, 'both', 'REJECTED');
+ const card = document.querySelector(`.photo-verification-card--${id}`);
+ const message = document.createElement('p');
+ message.innerText = res.data.message;
+
+ card.appendChild(message);
+
+ if (res.statusCode === 200) {
+ setTimeout(() => {
+ card.remove();
+ }, 2000);
+ }
+
+ setTimeout(() => {
+ window.location.reload();
+ }, 4000);
+}
+
+function notifyPhotoVerificationRequest(id) {
+ console.log(id, 'method to be implemented on the backend');
+}
+
+function createPhotoVerificationRequestImageBox(
+ prevImage,
+ newImage,
+ discordImage,
+) {
+ const imageBox = document.createElement('div');
+ imageBox.className = 'photo-verification-image-box';
+
+ const prevImageBox = document.createElement('div');
+ prevImageBox.className = 'photo-verification-image-box__block';
+ prevImageBox.innerHTML = `Previous Image
`;
+ imageBox.appendChild(prevImageBox);
+
+ const newImageBox = document.createElement('div');
+ newImageBox.className = 'photo-verification-image-box__block';
+ newImageBox.innerHTML = `New Image
`;
+ imageBox.appendChild(newImageBox);
+
+ const discordImageBox = document.createElement('div');
+ discordImageBox.className = 'photo-verification-image-box__block';
+ discordImageBox.innerHTML = `Discord Image
`;
+ imageBox.appendChild(discordImageBox);
+
+ return imageBox;
+}
+
+function createPhotoVerificationRequestButtonBox(
+ approvePhoto,
+ rejectPhoto,
+ notifyPhoto,
+ discordImageStatus,
+ profileImageStatus,
+) {
+ const buttonBox = document.createElement('div');
+ buttonBox.className = 'photo-verification-button-box';
+
+ if (!discordImageStatus) {
+ const approveDiscordButton = document.createElement('button');
+ approveDiscordButton.innerText = 'Approve Discord';
+ approveDiscordButton.onclick = () => approvePhoto('discord');
+ buttonBox.appendChild(approveDiscordButton);
+ }
+
+ if (!profileImageStatus) {
+ const approveProfileButton = document.createElement('button');
+ approveProfileButton.innerText = 'Approve Profile';
+ approveProfileButton.onclick = () => approvePhoto('profile');
+ buttonBox.appendChild(approveProfileButton);
+ }
+
+ const approveBothButton = document.createElement('button');
+ approveBothButton.innerText = 'Approve Both';
+ approveBothButton.onclick = () => approvePhoto('both');
+ buttonBox.appendChild(approveBothButton);
+
+ const rejectButton = document.createElement('button');
+ rejectButton.innerText = 'Reject';
+ rejectButton.className = 'reject-button';
+ rejectButton.onclick = rejectPhoto;
+ buttonBox.appendChild(rejectButton);
+
+ const notifyButton = document.createElement('button');
+ notifyButton.innerText = 'Notify User';
+ notifyButton.onclick = notifyPhoto;
+ notifyButton.className = 'notify-button';
+ notifyButton.disabled = true;
+
+ buttonBox.appendChild(notifyButton);
+ return buttonBox;
+}
+
+function createPhotoVerificationRequestStatusBox(
+ discordImageStatus,
+ profileImageStatus,
+) {
+ const statusBox = document.createElement('div');
+ statusBox.className = 'photo-verification-status-box';
+
+ const statusHeading = document.createElement('h3');
+ statusHeading.innerText = 'Status';
+ statusBox.appendChild(statusHeading);
+
+ const statusBoxContent = document.createElement('div');
+ statusBoxContent.className = `photo-verification-status-box__block`;
+ statusBoxContent.innerHTML = `Discord Image - ${
+ discordImageStatus ? 'Approved' : 'Pending'
+ }
Profile Image - ${
+ profileImageStatus ? 'Approved' : 'Pending'
+ }
`;
+ statusBox.appendChild(statusBoxContent);
+
+ return statusBox;
+}
+
+function createPhotoVerificationRequestCard(photoVerificationRequest) {
+ const card = document.createElement('div');
+ card.className = `photo-verification-card photo-verification-card--${photoVerificationRequest.id}`;
+
+ const heading = document.createElement('h3');
+ heading.innerText = `Photo Verifcation for ${photoVerificationRequest.user?.username}`;
+ card.appendChild(heading);
+
+ card.appendChild(
+ createPhotoVerificationRequestImageBox(
+ photoVerificationRequest.user.picture,
+ photoVerificationRequest.profile.url,
+ photoVerificationRequest.discord.url,
+ ),
+ );
+
+ card.appendChild(
+ createPhotoVerificationRequestStatusBox(
+ photoVerificationRequest.discord.approved,
+ photoVerificationRequest.profile.approved,
+ ),
+ );
+
+ card.appendChild(
+ createPhotoVerificationRequestButtonBox(
+ (imageType) =>
+ approvePhotoVerificationRequest(
+ photoVerificationRequest.id,
+ photoVerificationRequest.userId,
+ imageType,
+ ),
+ () =>
+ rejectPhotoVerificationRequest(
+ photoVerificationRequest.id,
+ photoVerificationRequest.userId,
+ ),
+ () => notifyPhotoVerificationRequest(photoVerificationRequest.id),
+ photoVerificationRequest.discord.approved,
+ photoVerificationRequest.profile.approved,
+ ),
+ );
+
+ return card;
+}
+
+async function getPhotoVerificationRequests(username) {
+ let url = `${API_BASE_URL}/users/picture/all/`;
+ if (username) {
+ url += `?username=${username}`;
+ }
+
+ const response = await fetch(url, {
+ credentials: 'include',
+ method: 'GET',
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ });
+ return await response.json();
+}
+
+async function modifyPhotoVerificationRequest(userId, imageType, status) {
+ const response = await fetch(
+ `${API_BASE_URL}/users/picture/verify/${userId}/?status=${status}&type=${imageType}`,
+ {
+ credentials: 'include',
+ method: 'PATCH',
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ },
+ );
+ return { data: await response.json(), statusCode: response.status };
+}
+
+function debounce(func, wait) {
+ let timeout;
+ return function executedFunction(...args) {
+ const later = () => {
+ clearTimeout(timeout);
+ func(...args);
+ };
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+}
diff --git a/photo-verification-requests/script.js b/photo-verification-requests/script.js
new file mode 100644
index 00000000..09b556bb
--- /dev/null
+++ b/photo-verification-requests/script.js
@@ -0,0 +1,35 @@
+const photoVerificationRequestContainer = document.querySelector(
+ '.photo-verification-requests',
+);
+const userSearchInput = document.querySelector('#user-search');
+
+async function render() {
+ const photoVerificationRequests = await getPhotoVerificationRequests();
+ const photoVerificationRequestObjects = photoVerificationRequests.data;
+ photoVerificationRequestObjects.forEach((obj) => {
+ photoVerificationRequestContainer.append(
+ createPhotoVerificationRequestCard(obj),
+ );
+ });
+}
+
+render();
+
+async function onUserSearchInput(e) {
+ photoVerificationRequestContainer.innerHTML = '';
+ if (e.target.value === '') {
+ render();
+ }
+ const photoVerificationRequests = await getPhotoVerificationRequests(
+ e.target.value,
+ );
+ const photoVerificationRequestObjects = photoVerificationRequests.data;
+ photoVerificationRequestContainer.innerHTML = '';
+ photoVerificationRequestObjects.forEach((obj) => {
+ photoVerificationRequestContainer.append(
+ createPhotoVerificationRequestCard(obj),
+ );
+ });
+}
+
+userSearchInput.addEventListener('input', debounce(onUserSearchInput, 500));
diff --git a/photo-verification-requests/style.css b/photo-verification-requests/style.css
new file mode 100644
index 00000000..90a412a3
--- /dev/null
+++ b/photo-verification-requests/style.css
@@ -0,0 +1,177 @@
+:root {
+ --blue-color: #1d1283;
+ --blue-hover-color: #11085c;
+ --dark-blue: #1b1378;
+ --light-aqua: #d4f9f2;
+ --scandal: #e5fcf5;
+ --green-transparent: rgba(0, 255, 0, 0.2);
+ --green-color: green;
+ --red-transparent: rgba(255, 0, 0, 0.145);
+ --white: #ffffff;
+ --black-transparent: #000000a8;
+ --black: #181717;
+ --light-gray: #d9d9d9;
+ --razzmatazz: #df0057;
+ --red-color: red;
+ --gray: #808080;
+ --button-proceed: #008000;
+ --modal-color: #00000048;
+ --black-color: black;
+ --light-gray-color: lightgray;
+ --green10: #e1f9f1;
+ --green500: #19805e;
+ --secondary10: #fff0f6;
+ --secondary600: #b6004e;
+ --medium-gray: #aeaeae;
+ --dark-gray: #737373;
+ --blue-color-heading: #041187;
+ --white-gray: #f2f2f3;
+ --color-red: #ae1820;
+ --color-green: rgba(0, 128, 0, 0.8);
+ --color-warn: rgba(199, 129, 18, 0.8);
+}
+
+*,
+::after,
+::before {
+ box-sizing: border-box;
+ font-family: monospace;
+ margin: 0;
+ padding: 0;
+}
+.header {
+ background-color: var(--dark-blue);
+ text-align: center;
+ color: var(--white);
+ padding: 2rem;
+}
+
+.search-filter {
+ display: flex;
+ justify-content: end;
+ align-items: center;
+ width: 100%;
+ padding: 2.5rem;
+ gap: 1rem;
+}
+
+#user-search {
+ width: 90%;
+ max-width: 15rem;
+ min-width: 10rem;
+ padding: 0.7rem 0.7rem;
+ border-radius: 0.4rem;
+ border: 2.5px solid var(--black-color);
+ font-size: medium;
+ background-color: var(--light-gray-color);
+ margin: 0 10px;
+ height: 2.5rem;
+}
+
+.container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.photo-verification-requests {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.photo-verification-card {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ width: min-content;
+ padding: 1.5rem;
+ border-radius: 10px;
+ text-align: left;
+ border: 1px solid var(--medium-gray);
+ position: relative;
+ max-width: 46rem;
+ width: 100%;
+ gap: 1rem;
+ max-height: 100%;
+}
+
+.photo-verification-card > h3 {
+ font-size: 1.5rem;
+ font-weight: 500;
+}
+
+.photo-verification-image-box {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 20px;
+ gap: 1.5rem;
+ width: 100%;
+}
+
+.photo-verification-image-box__block {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ gap: 5px;
+}
+
+.photo-verification-image-box__block > img {
+ width: 150px;
+ aspect-ratio: 1/1;
+ object-fit: cover;
+}
+
+.photo-verification-button-box {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ gap: 1.2rem;
+ margin-top: 20px;
+ margin-left: auto;
+ flex-wrap: wrap;
+}
+
+.photo-verification-button-box > button {
+ padding: 10px 20px;
+ border: none;
+ border-radius: 5px;
+ background-color: var(--green500);
+ color: var(--white);
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.photo-verification-button-box > .reject-button {
+ background-color: var(--color-red);
+}
+.photo-verification-button-box > .notify-button {
+ background-color: var(--blue-color);
+}
+
+.photo-verification-button-box > button:hover {
+ opacity: 0.9;
+}
+
+button:disabled,
+button:disabled:hover {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.photo-verification-status-box {
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+ gap: 10px;
+ margin-top: 20px;
+ flex-direction: column;
+ width: 100%;
+}
diff --git a/script.js b/script.js
index cd68ce22..5db3381a 100644
--- a/script.js
+++ b/script.js
@@ -2,6 +2,9 @@ const userManagementLink = document.getElementById(USER_MANAGEMENT_LINK);
const discordUserLink = document.getElementById('discord-user-link');
const extensionRequestsLink = document.getElementById(EXTENSION_REQUESTS_LINK);
const syncUsersStatusButton = document.getElementById(SYNC_USERS_STATUS);
+const photoVerificationRequestsButton = document.getElementById(
+ PHOTO_VERIFICATION_REQUESTS_BUTTON,
+);
const UpdatedstatusMessage = 'All repos uptodate';
const syncExternalAccountsButton = document.getElementById(
SYNC_EXTERNAL_ACCOUNTS,
@@ -95,6 +98,7 @@ showSuperUserOptions(
extensionRequestsLink,
discordUserLink,
taskRequestsLink,
+ photoVerificationRequestsButton,
);
const createGoalButton = document.getElementById('create-goal');
From 48f28a45acfd18f1959b2c803efae1da5233ed3b Mon Sep 17 00:00:00 2001
From: Vinayak Goyal <73058928+ivinayakg@users.noreply.github.com>
Date: Sat, 13 Apr 2024 04:02:26 +0530
Subject: [PATCH 2/5] test: tests written for photo verification flow
---
.../photo-verification.test.js | 269 ++++++++++++++++++
mock-data/photo-verification/index.js | 114 ++++++++
photo-verification-requests/local-utils.js | 48 +++-
photo-verification-requests/style.css | 3 +-
4 files changed, 427 insertions(+), 7 deletions(-)
create mode 100644 __tests__/photo-verification/photo-verification.test.js
create mode 100644 mock-data/photo-verification/index.js
diff --git a/__tests__/photo-verification/photo-verification.test.js b/__tests__/photo-verification/photo-verification.test.js
new file mode 100644
index 00000000..109c5aea
--- /dev/null
+++ b/__tests__/photo-verification/photo-verification.test.js
@@ -0,0 +1,269 @@
+const puppeteer = require('puppeteer');
+const {
+ photoVerificationRequestApprovedResponse,
+ photoVerificationRequestRejectedResponse,
+ photoVerificationRequestsListPending,
+ photoVerificationRequestsListUserSearch,
+ photoVerificationRequestDiscordUpdateResponse,
+} = require('../../mock-data/photo-verification');
+// const {
+// extensionRequestLogs,
+// extensionRequestLogsInSentence,
+// } = require('../../mock-data/logs');
+// const {
+// userSunny,
+// userRandhir,
+// allUsersData,
+// superUserForAudiLogs,
+// searchedUserForAuditLogs,
+// } = require('../../mock-data/users');
+// const { usersStatus } = require('../../mock-data/users-status');
+const baseUrl = 'http://localhost:8000/photo-verification-requests';
+
+describe('Tests the Photo Verification Screen', () => {
+ let browser;
+ let page;
+ let title;
+ let searchBar;
+ let photoVerificationRequestsElement;
+ 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://api.realdevsquad.com/users/picture/all/') {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestsListPending),
+ });
+ } else if (
+ url ===
+ 'https://api.realdevsquad.com/users/picture/all/?username=vinayak-g'
+ ) {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestsListUserSearch),
+ });
+ } else if (
+ url ===
+ `https://api.realdevsquad.com/users/picture/verify/${photoVerificationRequestsListPending.data[0].userId}/?status=APPROVED&type=both`
+ ) {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestApprovedResponse),
+ });
+ } else if (
+ url ===
+ `https://api.realdevsquad.com/users/picture/verify/${photoVerificationRequestsListPending.data[0].userId}/?status=REJECTED&type=both`
+ ) {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestRejectedResponse),
+ });
+ } else if (
+ url ===
+ `https://api.realdevsquad.com/discord-actions/avatar/update/${photoVerificationRequestsListPending.data[0].discordId}`
+ ) {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestDiscordUpdateResponse),
+ });
+ } else {
+ interceptedRequest.continue();
+ }
+ });
+
+ await page.goto(baseUrl);
+
+ await page.waitForNetworkIdle();
+
+ title = await page.$('.header');
+ searchBar = await page.$('#search');
+ photoVerificationRequestsElement = await page.$(
+ '.photo-verification-requests',
+ );
+ });
+
+ afterEach(async () => {
+ await page.goto('http://localhost:8000/photo-verification-requests');
+ await page.waitForNetworkIdle();
+ });
+ afterAll(async () => {
+ await browser.close();
+ });
+ it('Checks the UI elements on photo verification requests listing page', async () => {
+ title = await page.$('.header');
+ searchBar = await page.$('#search');
+ photoVerificationRequestCardList = await page.$$(
+ '.photo-verification-card',
+ );
+ photoVerificationRequestsElement = await page.$(
+ '.photo-verification-requests',
+ );
+ expect(title).toBeTruthy();
+ expect(searchBar).toBeTruthy();
+ expect(photoVerificationRequestCardList.length).toBe(2);
+ expect(photoVerificationRequestsElement).toBeTruthy();
+ });
+
+ it('checks the search functionality', async () => {
+ await page.type('#user-search', 'vinayak-g');
+ await page.keyboard.press('Enter');
+ await page.waitForNetworkIdle();
+
+ const cardsList = await page.$$('.photo-verification-card');
+ expect(cardsList.length).toBe(1);
+ const cardTextContent = await page.evaluate(
+ (element) => element.textContent,
+ cardsList[0],
+ );
+ expect(cardTextContent).toContain('vinayak-g');
+ });
+
+ it('checks the refresh discord avatar image functionality', async () => {
+ const photoVerificationRequestId =
+ photoVerificationRequestsListPending.data[0].id;
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ const refreshButton = await photoVerificationRequestCard.$(
+ '.refresh-discord-avatar-button',
+ );
+
+ await refreshButton.click();
+ // wait for 500ms for the request to complete
+ await page.waitForTimeout(500);
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ const discordImage = await photoVerificationRequestCard.$(
+ '.photo-verification-image-box__block--discord',
+ );
+
+ const discordImageSrc = await page.evaluate(
+ (element) => element.querySelector('img').src,
+ discordImage,
+ );
+
+ expect(discordImageSrc).toBe(
+ photoVerificationRequestDiscordUpdateResponse.discordAvatarUrl,
+ );
+ });
+ it('checks the reject photo verification functionality', async () => {
+ const photoVerificationRequestId =
+ photoVerificationRequestsListPending.data[0].id;
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ const rejectButton = await photoVerificationRequestCard.$('.reject-button');
+
+ await rejectButton.click();
+ // wait for 2500ms for the request to complete
+ await page.waitForTimeout(2500);
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ expect(photoVerificationRequestCard).toBe(null);
+ });
+
+ it('checks the reject photo verification functionality', async () => {
+ await page.evaluate(() => {
+ window.location.reload = () => {
+ console.log('window.location.reload was called');
+ };
+ });
+ const photoVerificationRequestId =
+ photoVerificationRequestsListPending.data[0].id;
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ const approveButton = await photoVerificationRequestCard.$(
+ '.approve-both-button',
+ );
+
+ await approveButton.click();
+ // wait for 500ms for the request to complete
+ await page.waitForTimeout(500);
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ responseMessage = await photoVerificationRequestCard.$eval(
+ 'p',
+ (el) => el.textContent,
+ );
+
+ expect(responseMessage).toBe(
+ photoVerificationRequestApprovedResponse.message,
+ );
+ });
+
+ it('Checks details of the first photo verification card', async () => {
+ photoVerificationRequestCardList = await page.$$(
+ '.photo-verification-card',
+ );
+
+ const firstPhotoVerificationRequestCard =
+ photoVerificationRequestCardList[0];
+
+ const titleText = await firstPhotoVerificationRequestCard.$eval(
+ 'h3',
+ (el) => el.textContent,
+ );
+ expect(titleText).toBe(
+ `Photo Verifcation for ${photoVerificationRequestsListPending.data[0].user.username}`,
+ );
+ });
+});
diff --git a/mock-data/photo-verification/index.js b/mock-data/photo-verification/index.js
new file mode 100644
index 00000000..8c3a9175
--- /dev/null
+++ b/mock-data/photo-verification/index.js
@@ -0,0 +1,114 @@
+const photoVerificationRequestsListPending = {
+ message: 'User image verification record fetched successfully!',
+ data: [
+ {
+ id: 'BEtG17kv0H27lWOGwTZX',
+ discordId: '238947756238758',
+ userId: '8o7f87h3u4h98fh9843hf834',
+ discord: {
+ date: {
+ _seconds: 1712788779,
+ _nanoseconds: 192000000,
+ },
+ url: 'https://cdn.discordapp.com/avatars/238947756238758/b0a2691fd3e05c28858f435f11f02e41.png',
+ approved: false,
+ },
+ profile: {
+ date: {
+ _seconds: 1712788779,
+ _nanoseconds: 192000000,
+ },
+ url: 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ publicId: 'profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf',
+ approved: false,
+ },
+ status: 'PENDING',
+ user: {
+ username: 'vinayak-g',
+ picture:
+ 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ },
+ },
+ {
+ id: 'X1Kua1HeUqtlX5Z6xwSq',
+ discordId: '238947756238758',
+ userId: '8o7f87h3u4h98fh9843hf834',
+ status: 'PENDING',
+ discord: {
+ date: {
+ _seconds: 1712835230,
+ _nanoseconds: 7000000,
+ },
+ approved: false,
+ url: 'https://cdn.discordapp.com/avatars/238947756238758/b0a2691fd3e05c28858f435f11f02e41.png',
+ },
+ profile: {
+ date: {
+ _seconds: 1712835230,
+ _nanoseconds: 7000000,
+ },
+ approved: false,
+ },
+ user: {
+ username: 'vinayak-g',
+ picture:
+ 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ },
+ },
+ ],
+};
+
+const photoVerificationRequestsListUserSearch = {
+ message: 'User image verification record fetched successfully!',
+ data: [
+ {
+ discordId: '238947756238758',
+ userId: '8o7f87h3u4h98fh9843hf834',
+ discord: {
+ date: {
+ _seconds: 1712788779,
+ _nanoseconds: 192000000,
+ },
+ url: 'https://cdn.discordapp.com/avatars/238947756238758/b0a2691fd3e05c28858f435f11f02e41.png',
+ approved: true,
+ },
+ profile: {
+ date: {
+ _seconds: 1712788779,
+ _nanoseconds: 192000000,
+ },
+ url: 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ publicId: 'profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf',
+ approved: false,
+ },
+ status: 'PENDING',
+ user: {
+ username: 'vinayak-g',
+ picture:
+ 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ },
+ },
+ ],
+};
+
+const photoVerificationRequestApprovedResponse = {
+ message: 'User image data verified successfully',
+};
+
+const photoVerificationRequestRejectedResponse = {
+ message: 'User photo verification request rejected successfully',
+};
+
+const photoVerificationRequestDiscordUpdateResponse = {
+ message: 'Discord avatar URL updated successfully!',
+ discordAvatarUrl:
+ 'https://cdn.discordapp.com/avatars/238947756238758/b0a2691fd3e05c28858f435f11f02e41_new.png',
+};
+
+module.exports = {
+ photoVerificationRequestApprovedResponse,
+ photoVerificationRequestRejectedResponse,
+ photoVerificationRequestsListPending,
+ photoVerificationRequestsListUserSearch,
+ photoVerificationRequestDiscordUpdateResponse,
+};
diff --git a/photo-verification-requests/local-utils.js b/photo-verification-requests/local-utils.js
index 80413af5..492b9e65 100644
--- a/photo-verification-requests/local-utils.js
+++ b/photo-verification-requests/local-utils.js
@@ -1,3 +1,15 @@
+async function refreshDiscordImage(discordId, id) {
+ const res = await refreshDiscordImageRequest(discordId);
+ const card = document.querySelector(`.photo-verification-card--${id}`);
+ const discordImage = card.querySelector(
+ '.photo-verification-image-box__block--discord img',
+ );
+
+ if (res.statusCode === 200) {
+ discordImage.src = res.data.discordAvatarUrl;
+ }
+}
+
async function approvePhotoVerificationRequest(id, userId, imageType) {
const res = await modifyPhotoVerificationRequest(
userId,
@@ -28,10 +40,6 @@ async function rejectPhotoVerificationRequest(id, userId) {
card.remove();
}, 2000);
}
-
- setTimeout(() => {
- window.location.reload();
- }, 4000);
}
function notifyPhotoVerificationRequest(id) {
@@ -57,7 +65,8 @@ function createPhotoVerificationRequestImageBox(
imageBox.appendChild(newImageBox);
const discordImageBox = document.createElement('div');
- discordImageBox.className = 'photo-verification-image-box__block';
+ discordImageBox.className =
+ 'photo-verification-image-box__block photo-verification-image-box__block--discord';
discordImageBox.innerHTML = `Discord Image
`;
imageBox.appendChild(discordImageBox);
@@ -68,6 +77,7 @@ function createPhotoVerificationRequestButtonBox(
approvePhoto,
rejectPhoto,
notifyPhoto,
+ refreshDiscordAvatarPhoto,
discordImageStatus,
profileImageStatus,
) {
@@ -90,6 +100,7 @@ function createPhotoVerificationRequestButtonBox(
const approveBothButton = document.createElement('button');
approveBothButton.innerText = 'Approve Both';
+ approveBothButton.className = 'approve-both-button';
approveBothButton.onclick = () => approvePhoto('both');
buttonBox.appendChild(approveBothButton);
@@ -99,13 +110,19 @@ function createPhotoVerificationRequestButtonBox(
rejectButton.onclick = rejectPhoto;
buttonBox.appendChild(rejectButton);
+ const refreshDiscordAvatarButton = document.createElement('button');
+ refreshDiscordAvatarButton.innerText = 'Refresh Discord Image';
+ refreshDiscordAvatarButton.onclick = refreshDiscordAvatarPhoto;
+ refreshDiscordAvatarButton.className = 'refresh-discord-avatar-button';
+ buttonBox.appendChild(refreshDiscordAvatarButton);
+
const notifyButton = document.createElement('button');
notifyButton.innerText = 'Notify User';
notifyButton.onclick = notifyPhoto;
notifyButton.className = 'notify-button';
notifyButton.disabled = true;
-
buttonBox.appendChild(notifyButton);
+
return buttonBox;
}
@@ -169,6 +186,11 @@ function createPhotoVerificationRequestCard(photoVerificationRequest) {
photoVerificationRequest.userId,
),
() => notifyPhotoVerificationRequest(photoVerificationRequest.id),
+ () =>
+ refreshDiscordImage(
+ photoVerificationRequest.discordId,
+ photoVerificationRequest.id,
+ ),
photoVerificationRequest.discord.approved,
photoVerificationRequest.profile.approved,
),
@@ -207,6 +229,20 @@ async function modifyPhotoVerificationRequest(userId, imageType, status) {
return { data: await response.json(), statusCode: response.status };
}
+async function refreshDiscordImageRequest(discordId) {
+ const response = await fetch(
+ `${API_BASE_URL}/discord-actions/avatar/update/${discordId}`,
+ {
+ credentials: 'include',
+ method: 'PATCH',
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ },
+ );
+ return { data: await response.json(), statusCode: response.status };
+}
+
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
diff --git a/photo-verification-requests/style.css b/photo-verification-requests/style.css
index 90a412a3..107c4add 100644
--- a/photo-verification-requests/style.css
+++ b/photo-verification-requests/style.css
@@ -152,7 +152,8 @@
.photo-verification-button-box > .reject-button {
background-color: var(--color-red);
}
-.photo-verification-button-box > .notify-button {
+.photo-verification-button-box > .notify-button,
+.photo-verification-button-box > .refresh-discord-avatar-button {
background-color: var(--blue-color);
}
From 62f63ff5b43f961f3e0e4933f1097d8a0e08d54d Mon Sep 17 00:00:00 2001
From: Vinayak Goyal <73058928+ivinayakg@users.noreply.github.com>
Date: Thu, 11 Apr 2024 04:44:15 +0530
Subject: [PATCH 3/5] feat: photo verification dashboard done
---
constants.js | 1 +
index.html | 7 +
photo-verification-requests/index.html | 31 +++
photo-verification-requests/local-utils.js | 220 +++++++++++++++++++++
photo-verification-requests/script.js | 35 ++++
photo-verification-requests/style.css | 177 +++++++++++++++++
script.js | 4 +
7 files changed, 475 insertions(+)
create mode 100644 photo-verification-requests/index.html
create mode 100644 photo-verification-requests/local-utils.js
create mode 100644 photo-verification-requests/script.js
create mode 100644 photo-verification-requests/style.css
diff --git a/constants.js b/constants.js
index 2095f752..c9387fcd 100644
--- a/constants.js
+++ b/constants.js
@@ -27,6 +27,7 @@ const DISABLED = 'disabled';
const STATUS_BASE_URL_PROD = 'https://status.realdevsquad.com';
const STATUS_BASE_URL_STAGING = 'https://staging-status.realdevsquad.com';
const STATUS_BASE_URL = STATUS_BASE_URL_PROD;
+const PHOTO_VERIFICATION_REQUESTS_BUTTON = 'photo-verification-requests-button';
const dummyPicture = 'https://dashboard.realdevsquad.com/images/avatar.png';
const USER_MANAGEMENT_URL =
diff --git a/index.html b/index.html
index cf325060..1eab8553 100644
--- a/index.html
+++ b/index.html
@@ -138,6 +138,13 @@
>
Extension Requests
+
+ Photo Verification Requests
+
+
+
+
+
+
+ Photo Verification Requests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/photo-verification-requests/local-utils.js b/photo-verification-requests/local-utils.js
new file mode 100644
index 00000000..80413af5
--- /dev/null
+++ b/photo-verification-requests/local-utils.js
@@ -0,0 +1,220 @@
+async function approvePhotoVerificationRequest(id, userId, imageType) {
+ const res = await modifyPhotoVerificationRequest(
+ userId,
+ imageType,
+ 'APPROVED',
+ );
+ const card = document.querySelector(`.photo-verification-card--${id}`);
+ const message = document.createElement('p');
+ message.innerText = res.data.message;
+
+ card.appendChild(message);
+
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+}
+
+async function rejectPhotoVerificationRequest(id, userId) {
+ const res = await modifyPhotoVerificationRequest(userId, 'both', 'REJECTED');
+ const card = document.querySelector(`.photo-verification-card--${id}`);
+ const message = document.createElement('p');
+ message.innerText = res.data.message;
+
+ card.appendChild(message);
+
+ if (res.statusCode === 200) {
+ setTimeout(() => {
+ card.remove();
+ }, 2000);
+ }
+
+ setTimeout(() => {
+ window.location.reload();
+ }, 4000);
+}
+
+function notifyPhotoVerificationRequest(id) {
+ console.log(id, 'method to be implemented on the backend');
+}
+
+function createPhotoVerificationRequestImageBox(
+ prevImage,
+ newImage,
+ discordImage,
+) {
+ const imageBox = document.createElement('div');
+ imageBox.className = 'photo-verification-image-box';
+
+ const prevImageBox = document.createElement('div');
+ prevImageBox.className = 'photo-verification-image-box__block';
+ prevImageBox.innerHTML = `Previous Image
`;
+ imageBox.appendChild(prevImageBox);
+
+ const newImageBox = document.createElement('div');
+ newImageBox.className = 'photo-verification-image-box__block';
+ newImageBox.innerHTML = `New Image
`;
+ imageBox.appendChild(newImageBox);
+
+ const discordImageBox = document.createElement('div');
+ discordImageBox.className = 'photo-verification-image-box__block';
+ discordImageBox.innerHTML = `Discord Image
`;
+ imageBox.appendChild(discordImageBox);
+
+ return imageBox;
+}
+
+function createPhotoVerificationRequestButtonBox(
+ approvePhoto,
+ rejectPhoto,
+ notifyPhoto,
+ discordImageStatus,
+ profileImageStatus,
+) {
+ const buttonBox = document.createElement('div');
+ buttonBox.className = 'photo-verification-button-box';
+
+ if (!discordImageStatus) {
+ const approveDiscordButton = document.createElement('button');
+ approveDiscordButton.innerText = 'Approve Discord';
+ approveDiscordButton.onclick = () => approvePhoto('discord');
+ buttonBox.appendChild(approveDiscordButton);
+ }
+
+ if (!profileImageStatus) {
+ const approveProfileButton = document.createElement('button');
+ approveProfileButton.innerText = 'Approve Profile';
+ approveProfileButton.onclick = () => approvePhoto('profile');
+ buttonBox.appendChild(approveProfileButton);
+ }
+
+ const approveBothButton = document.createElement('button');
+ approveBothButton.innerText = 'Approve Both';
+ approveBothButton.onclick = () => approvePhoto('both');
+ buttonBox.appendChild(approveBothButton);
+
+ const rejectButton = document.createElement('button');
+ rejectButton.innerText = 'Reject';
+ rejectButton.className = 'reject-button';
+ rejectButton.onclick = rejectPhoto;
+ buttonBox.appendChild(rejectButton);
+
+ const notifyButton = document.createElement('button');
+ notifyButton.innerText = 'Notify User';
+ notifyButton.onclick = notifyPhoto;
+ notifyButton.className = 'notify-button';
+ notifyButton.disabled = true;
+
+ buttonBox.appendChild(notifyButton);
+ return buttonBox;
+}
+
+function createPhotoVerificationRequestStatusBox(
+ discordImageStatus,
+ profileImageStatus,
+) {
+ const statusBox = document.createElement('div');
+ statusBox.className = 'photo-verification-status-box';
+
+ const statusHeading = document.createElement('h3');
+ statusHeading.innerText = 'Status';
+ statusBox.appendChild(statusHeading);
+
+ const statusBoxContent = document.createElement('div');
+ statusBoxContent.className = `photo-verification-status-box__block`;
+ statusBoxContent.innerHTML = `Discord Image - ${
+ discordImageStatus ? 'Approved' : 'Pending'
+ }
Profile Image - ${
+ profileImageStatus ? 'Approved' : 'Pending'
+ }
`;
+ statusBox.appendChild(statusBoxContent);
+
+ return statusBox;
+}
+
+function createPhotoVerificationRequestCard(photoVerificationRequest) {
+ const card = document.createElement('div');
+ card.className = `photo-verification-card photo-verification-card--${photoVerificationRequest.id}`;
+
+ const heading = document.createElement('h3');
+ heading.innerText = `Photo Verifcation for ${photoVerificationRequest.user?.username}`;
+ card.appendChild(heading);
+
+ card.appendChild(
+ createPhotoVerificationRequestImageBox(
+ photoVerificationRequest.user.picture,
+ photoVerificationRequest.profile.url,
+ photoVerificationRequest.discord.url,
+ ),
+ );
+
+ card.appendChild(
+ createPhotoVerificationRequestStatusBox(
+ photoVerificationRequest.discord.approved,
+ photoVerificationRequest.profile.approved,
+ ),
+ );
+
+ card.appendChild(
+ createPhotoVerificationRequestButtonBox(
+ (imageType) =>
+ approvePhotoVerificationRequest(
+ photoVerificationRequest.id,
+ photoVerificationRequest.userId,
+ imageType,
+ ),
+ () =>
+ rejectPhotoVerificationRequest(
+ photoVerificationRequest.id,
+ photoVerificationRequest.userId,
+ ),
+ () => notifyPhotoVerificationRequest(photoVerificationRequest.id),
+ photoVerificationRequest.discord.approved,
+ photoVerificationRequest.profile.approved,
+ ),
+ );
+
+ return card;
+}
+
+async function getPhotoVerificationRequests(username) {
+ let url = `${API_BASE_URL}/users/picture/all/`;
+ if (username) {
+ url += `?username=${username}`;
+ }
+
+ const response = await fetch(url, {
+ credentials: 'include',
+ method: 'GET',
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ });
+ return await response.json();
+}
+
+async function modifyPhotoVerificationRequest(userId, imageType, status) {
+ const response = await fetch(
+ `${API_BASE_URL}/users/picture/verify/${userId}/?status=${status}&type=${imageType}`,
+ {
+ credentials: 'include',
+ method: 'PATCH',
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ },
+ );
+ return { data: await response.json(), statusCode: response.status };
+}
+
+function debounce(func, wait) {
+ let timeout;
+ return function executedFunction(...args) {
+ const later = () => {
+ clearTimeout(timeout);
+ func(...args);
+ };
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+}
diff --git a/photo-verification-requests/script.js b/photo-verification-requests/script.js
new file mode 100644
index 00000000..09b556bb
--- /dev/null
+++ b/photo-verification-requests/script.js
@@ -0,0 +1,35 @@
+const photoVerificationRequestContainer = document.querySelector(
+ '.photo-verification-requests',
+);
+const userSearchInput = document.querySelector('#user-search');
+
+async function render() {
+ const photoVerificationRequests = await getPhotoVerificationRequests();
+ const photoVerificationRequestObjects = photoVerificationRequests.data;
+ photoVerificationRequestObjects.forEach((obj) => {
+ photoVerificationRequestContainer.append(
+ createPhotoVerificationRequestCard(obj),
+ );
+ });
+}
+
+render();
+
+async function onUserSearchInput(e) {
+ photoVerificationRequestContainer.innerHTML = '';
+ if (e.target.value === '') {
+ render();
+ }
+ const photoVerificationRequests = await getPhotoVerificationRequests(
+ e.target.value,
+ );
+ const photoVerificationRequestObjects = photoVerificationRequests.data;
+ photoVerificationRequestContainer.innerHTML = '';
+ photoVerificationRequestObjects.forEach((obj) => {
+ photoVerificationRequestContainer.append(
+ createPhotoVerificationRequestCard(obj),
+ );
+ });
+}
+
+userSearchInput.addEventListener('input', debounce(onUserSearchInput, 500));
diff --git a/photo-verification-requests/style.css b/photo-verification-requests/style.css
new file mode 100644
index 00000000..90a412a3
--- /dev/null
+++ b/photo-verification-requests/style.css
@@ -0,0 +1,177 @@
+:root {
+ --blue-color: #1d1283;
+ --blue-hover-color: #11085c;
+ --dark-blue: #1b1378;
+ --light-aqua: #d4f9f2;
+ --scandal: #e5fcf5;
+ --green-transparent: rgba(0, 255, 0, 0.2);
+ --green-color: green;
+ --red-transparent: rgba(255, 0, 0, 0.145);
+ --white: #ffffff;
+ --black-transparent: #000000a8;
+ --black: #181717;
+ --light-gray: #d9d9d9;
+ --razzmatazz: #df0057;
+ --red-color: red;
+ --gray: #808080;
+ --button-proceed: #008000;
+ --modal-color: #00000048;
+ --black-color: black;
+ --light-gray-color: lightgray;
+ --green10: #e1f9f1;
+ --green500: #19805e;
+ --secondary10: #fff0f6;
+ --secondary600: #b6004e;
+ --medium-gray: #aeaeae;
+ --dark-gray: #737373;
+ --blue-color-heading: #041187;
+ --white-gray: #f2f2f3;
+ --color-red: #ae1820;
+ --color-green: rgba(0, 128, 0, 0.8);
+ --color-warn: rgba(199, 129, 18, 0.8);
+}
+
+*,
+::after,
+::before {
+ box-sizing: border-box;
+ font-family: monospace;
+ margin: 0;
+ padding: 0;
+}
+.header {
+ background-color: var(--dark-blue);
+ text-align: center;
+ color: var(--white);
+ padding: 2rem;
+}
+
+.search-filter {
+ display: flex;
+ justify-content: end;
+ align-items: center;
+ width: 100%;
+ padding: 2.5rem;
+ gap: 1rem;
+}
+
+#user-search {
+ width: 90%;
+ max-width: 15rem;
+ min-width: 10rem;
+ padding: 0.7rem 0.7rem;
+ border-radius: 0.4rem;
+ border: 2.5px solid var(--black-color);
+ font-size: medium;
+ background-color: var(--light-gray-color);
+ margin: 0 10px;
+ height: 2.5rem;
+}
+
+.container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.photo-verification-requests {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.photo-verification-card {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ width: min-content;
+ padding: 1.5rem;
+ border-radius: 10px;
+ text-align: left;
+ border: 1px solid var(--medium-gray);
+ position: relative;
+ max-width: 46rem;
+ width: 100%;
+ gap: 1rem;
+ max-height: 100%;
+}
+
+.photo-verification-card > h3 {
+ font-size: 1.5rem;
+ font-weight: 500;
+}
+
+.photo-verification-image-box {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 20px;
+ gap: 1.5rem;
+ width: 100%;
+}
+
+.photo-verification-image-box__block {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ gap: 5px;
+}
+
+.photo-verification-image-box__block > img {
+ width: 150px;
+ aspect-ratio: 1/1;
+ object-fit: cover;
+}
+
+.photo-verification-button-box {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ gap: 1.2rem;
+ margin-top: 20px;
+ margin-left: auto;
+ flex-wrap: wrap;
+}
+
+.photo-verification-button-box > button {
+ padding: 10px 20px;
+ border: none;
+ border-radius: 5px;
+ background-color: var(--green500);
+ color: var(--white);
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.photo-verification-button-box > .reject-button {
+ background-color: var(--color-red);
+}
+.photo-verification-button-box > .notify-button {
+ background-color: var(--blue-color);
+}
+
+.photo-verification-button-box > button:hover {
+ opacity: 0.9;
+}
+
+button:disabled,
+button:disabled:hover {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.photo-verification-status-box {
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+ gap: 10px;
+ margin-top: 20px;
+ flex-direction: column;
+ width: 100%;
+}
diff --git a/script.js b/script.js
index cd68ce22..5db3381a 100644
--- a/script.js
+++ b/script.js
@@ -2,6 +2,9 @@ const userManagementLink = document.getElementById(USER_MANAGEMENT_LINK);
const discordUserLink = document.getElementById('discord-user-link');
const extensionRequestsLink = document.getElementById(EXTENSION_REQUESTS_LINK);
const syncUsersStatusButton = document.getElementById(SYNC_USERS_STATUS);
+const photoVerificationRequestsButton = document.getElementById(
+ PHOTO_VERIFICATION_REQUESTS_BUTTON,
+);
const UpdatedstatusMessage = 'All repos uptodate';
const syncExternalAccountsButton = document.getElementById(
SYNC_EXTERNAL_ACCOUNTS,
@@ -95,6 +98,7 @@ showSuperUserOptions(
extensionRequestsLink,
discordUserLink,
taskRequestsLink,
+ photoVerificationRequestsButton,
);
const createGoalButton = document.getElementById('create-goal');
From c6dcafb5bcd8b9116d20d995cb4a3d215b6272fb Mon Sep 17 00:00:00 2001
From: Vinayak Goyal <73058928+ivinayakg@users.noreply.github.com>
Date: Sat, 13 Apr 2024 04:02:26 +0530
Subject: [PATCH 4/5] test: tests written for photo verification flow
---
.../photo-verification.test.js | 269 ++++++++++++++++++
mock-data/photo-verification/index.js | 114 ++++++++
photo-verification-requests/local-utils.js | 48 +++-
photo-verification-requests/style.css | 3 +-
4 files changed, 427 insertions(+), 7 deletions(-)
create mode 100644 __tests__/photo-verification/photo-verification.test.js
create mode 100644 mock-data/photo-verification/index.js
diff --git a/__tests__/photo-verification/photo-verification.test.js b/__tests__/photo-verification/photo-verification.test.js
new file mode 100644
index 00000000..109c5aea
--- /dev/null
+++ b/__tests__/photo-verification/photo-verification.test.js
@@ -0,0 +1,269 @@
+const puppeteer = require('puppeteer');
+const {
+ photoVerificationRequestApprovedResponse,
+ photoVerificationRequestRejectedResponse,
+ photoVerificationRequestsListPending,
+ photoVerificationRequestsListUserSearch,
+ photoVerificationRequestDiscordUpdateResponse,
+} = require('../../mock-data/photo-verification');
+// const {
+// extensionRequestLogs,
+// extensionRequestLogsInSentence,
+// } = require('../../mock-data/logs');
+// const {
+// userSunny,
+// userRandhir,
+// allUsersData,
+// superUserForAudiLogs,
+// searchedUserForAuditLogs,
+// } = require('../../mock-data/users');
+// const { usersStatus } = require('../../mock-data/users-status');
+const baseUrl = 'http://localhost:8000/photo-verification-requests';
+
+describe('Tests the Photo Verification Screen', () => {
+ let browser;
+ let page;
+ let title;
+ let searchBar;
+ let photoVerificationRequestsElement;
+ 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://api.realdevsquad.com/users/picture/all/') {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestsListPending),
+ });
+ } else if (
+ url ===
+ 'https://api.realdevsquad.com/users/picture/all/?username=vinayak-g'
+ ) {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestsListUserSearch),
+ });
+ } else if (
+ url ===
+ `https://api.realdevsquad.com/users/picture/verify/${photoVerificationRequestsListPending.data[0].userId}/?status=APPROVED&type=both`
+ ) {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestApprovedResponse),
+ });
+ } else if (
+ url ===
+ `https://api.realdevsquad.com/users/picture/verify/${photoVerificationRequestsListPending.data[0].userId}/?status=REJECTED&type=both`
+ ) {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestRejectedResponse),
+ });
+ } else if (
+ url ===
+ `https://api.realdevsquad.com/discord-actions/avatar/update/${photoVerificationRequestsListPending.data[0].discordId}`
+ ) {
+ interceptedRequest.respond({
+ status: 200,
+ contentType: 'application/json',
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ body: JSON.stringify(photoVerificationRequestDiscordUpdateResponse),
+ });
+ } else {
+ interceptedRequest.continue();
+ }
+ });
+
+ await page.goto(baseUrl);
+
+ await page.waitForNetworkIdle();
+
+ title = await page.$('.header');
+ searchBar = await page.$('#search');
+ photoVerificationRequestsElement = await page.$(
+ '.photo-verification-requests',
+ );
+ });
+
+ afterEach(async () => {
+ await page.goto('http://localhost:8000/photo-verification-requests');
+ await page.waitForNetworkIdle();
+ });
+ afterAll(async () => {
+ await browser.close();
+ });
+ it('Checks the UI elements on photo verification requests listing page', async () => {
+ title = await page.$('.header');
+ searchBar = await page.$('#search');
+ photoVerificationRequestCardList = await page.$$(
+ '.photo-verification-card',
+ );
+ photoVerificationRequestsElement = await page.$(
+ '.photo-verification-requests',
+ );
+ expect(title).toBeTruthy();
+ expect(searchBar).toBeTruthy();
+ expect(photoVerificationRequestCardList.length).toBe(2);
+ expect(photoVerificationRequestsElement).toBeTruthy();
+ });
+
+ it('checks the search functionality', async () => {
+ await page.type('#user-search', 'vinayak-g');
+ await page.keyboard.press('Enter');
+ await page.waitForNetworkIdle();
+
+ const cardsList = await page.$$('.photo-verification-card');
+ expect(cardsList.length).toBe(1);
+ const cardTextContent = await page.evaluate(
+ (element) => element.textContent,
+ cardsList[0],
+ );
+ expect(cardTextContent).toContain('vinayak-g');
+ });
+
+ it('checks the refresh discord avatar image functionality', async () => {
+ const photoVerificationRequestId =
+ photoVerificationRequestsListPending.data[0].id;
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ const refreshButton = await photoVerificationRequestCard.$(
+ '.refresh-discord-avatar-button',
+ );
+
+ await refreshButton.click();
+ // wait for 500ms for the request to complete
+ await page.waitForTimeout(500);
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ const discordImage = await photoVerificationRequestCard.$(
+ '.photo-verification-image-box__block--discord',
+ );
+
+ const discordImageSrc = await page.evaluate(
+ (element) => element.querySelector('img').src,
+ discordImage,
+ );
+
+ expect(discordImageSrc).toBe(
+ photoVerificationRequestDiscordUpdateResponse.discordAvatarUrl,
+ );
+ });
+ it('checks the reject photo verification functionality', async () => {
+ const photoVerificationRequestId =
+ photoVerificationRequestsListPending.data[0].id;
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ const rejectButton = await photoVerificationRequestCard.$('.reject-button');
+
+ await rejectButton.click();
+ // wait for 2500ms for the request to complete
+ await page.waitForTimeout(2500);
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ expect(photoVerificationRequestCard).toBe(null);
+ });
+
+ it('checks the reject photo verification functionality', async () => {
+ await page.evaluate(() => {
+ window.location.reload = () => {
+ console.log('window.location.reload was called');
+ };
+ });
+ const photoVerificationRequestId =
+ photoVerificationRequestsListPending.data[0].id;
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ const approveButton = await photoVerificationRequestCard.$(
+ '.approve-both-button',
+ );
+
+ await approveButton.click();
+ // wait for 500ms for the request to complete
+ await page.waitForTimeout(500);
+
+ photoVerificationRequestCard = await page.$(
+ `.photo-verification-card--${photoVerificationRequestId}`,
+ );
+
+ responseMessage = await photoVerificationRequestCard.$eval(
+ 'p',
+ (el) => el.textContent,
+ );
+
+ expect(responseMessage).toBe(
+ photoVerificationRequestApprovedResponse.message,
+ );
+ });
+
+ it('Checks details of the first photo verification card', async () => {
+ photoVerificationRequestCardList = await page.$$(
+ '.photo-verification-card',
+ );
+
+ const firstPhotoVerificationRequestCard =
+ photoVerificationRequestCardList[0];
+
+ const titleText = await firstPhotoVerificationRequestCard.$eval(
+ 'h3',
+ (el) => el.textContent,
+ );
+ expect(titleText).toBe(
+ `Photo Verifcation for ${photoVerificationRequestsListPending.data[0].user.username}`,
+ );
+ });
+});
diff --git a/mock-data/photo-verification/index.js b/mock-data/photo-verification/index.js
new file mode 100644
index 00000000..8c3a9175
--- /dev/null
+++ b/mock-data/photo-verification/index.js
@@ -0,0 +1,114 @@
+const photoVerificationRequestsListPending = {
+ message: 'User image verification record fetched successfully!',
+ data: [
+ {
+ id: 'BEtG17kv0H27lWOGwTZX',
+ discordId: '238947756238758',
+ userId: '8o7f87h3u4h98fh9843hf834',
+ discord: {
+ date: {
+ _seconds: 1712788779,
+ _nanoseconds: 192000000,
+ },
+ url: 'https://cdn.discordapp.com/avatars/238947756238758/b0a2691fd3e05c28858f435f11f02e41.png',
+ approved: false,
+ },
+ profile: {
+ date: {
+ _seconds: 1712788779,
+ _nanoseconds: 192000000,
+ },
+ url: 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ publicId: 'profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf',
+ approved: false,
+ },
+ status: 'PENDING',
+ user: {
+ username: 'vinayak-g',
+ picture:
+ 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ },
+ },
+ {
+ id: 'X1Kua1HeUqtlX5Z6xwSq',
+ discordId: '238947756238758',
+ userId: '8o7f87h3u4h98fh9843hf834',
+ status: 'PENDING',
+ discord: {
+ date: {
+ _seconds: 1712835230,
+ _nanoseconds: 7000000,
+ },
+ approved: false,
+ url: 'https://cdn.discordapp.com/avatars/238947756238758/b0a2691fd3e05c28858f435f11f02e41.png',
+ },
+ profile: {
+ date: {
+ _seconds: 1712835230,
+ _nanoseconds: 7000000,
+ },
+ approved: false,
+ },
+ user: {
+ username: 'vinayak-g',
+ picture:
+ 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ },
+ },
+ ],
+};
+
+const photoVerificationRequestsListUserSearch = {
+ message: 'User image verification record fetched successfully!',
+ data: [
+ {
+ discordId: '238947756238758',
+ userId: '8o7f87h3u4h98fh9843hf834',
+ discord: {
+ date: {
+ _seconds: 1712788779,
+ _nanoseconds: 192000000,
+ },
+ url: 'https://cdn.discordapp.com/avatars/238947756238758/b0a2691fd3e05c28858f435f11f02e41.png',
+ approved: true,
+ },
+ profile: {
+ date: {
+ _seconds: 1712788779,
+ _nanoseconds: 192000000,
+ },
+ url: 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ publicId: 'profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf',
+ approved: false,
+ },
+ status: 'PENDING',
+ user: {
+ username: 'vinayak-g',
+ picture:
+ 'https://res.cloudinary.com/profile/8o7f87h3u4h98fh9843hf834/umgnk8o7ujrzbmybnwzf.jpg',
+ },
+ },
+ ],
+};
+
+const photoVerificationRequestApprovedResponse = {
+ message: 'User image data verified successfully',
+};
+
+const photoVerificationRequestRejectedResponse = {
+ message: 'User photo verification request rejected successfully',
+};
+
+const photoVerificationRequestDiscordUpdateResponse = {
+ message: 'Discord avatar URL updated successfully!',
+ discordAvatarUrl:
+ 'https://cdn.discordapp.com/avatars/238947756238758/b0a2691fd3e05c28858f435f11f02e41_new.png',
+};
+
+module.exports = {
+ photoVerificationRequestApprovedResponse,
+ photoVerificationRequestRejectedResponse,
+ photoVerificationRequestsListPending,
+ photoVerificationRequestsListUserSearch,
+ photoVerificationRequestDiscordUpdateResponse,
+};
diff --git a/photo-verification-requests/local-utils.js b/photo-verification-requests/local-utils.js
index 80413af5..492b9e65 100644
--- a/photo-verification-requests/local-utils.js
+++ b/photo-verification-requests/local-utils.js
@@ -1,3 +1,15 @@
+async function refreshDiscordImage(discordId, id) {
+ const res = await refreshDiscordImageRequest(discordId);
+ const card = document.querySelector(`.photo-verification-card--${id}`);
+ const discordImage = card.querySelector(
+ '.photo-verification-image-box__block--discord img',
+ );
+
+ if (res.statusCode === 200) {
+ discordImage.src = res.data.discordAvatarUrl;
+ }
+}
+
async function approvePhotoVerificationRequest(id, userId, imageType) {
const res = await modifyPhotoVerificationRequest(
userId,
@@ -28,10 +40,6 @@ async function rejectPhotoVerificationRequest(id, userId) {
card.remove();
}, 2000);
}
-
- setTimeout(() => {
- window.location.reload();
- }, 4000);
}
function notifyPhotoVerificationRequest(id) {
@@ -57,7 +65,8 @@ function createPhotoVerificationRequestImageBox(
imageBox.appendChild(newImageBox);
const discordImageBox = document.createElement('div');
- discordImageBox.className = 'photo-verification-image-box__block';
+ discordImageBox.className =
+ 'photo-verification-image-box__block photo-verification-image-box__block--discord';
discordImageBox.innerHTML = `Discord Image
`;
imageBox.appendChild(discordImageBox);
@@ -68,6 +77,7 @@ function createPhotoVerificationRequestButtonBox(
approvePhoto,
rejectPhoto,
notifyPhoto,
+ refreshDiscordAvatarPhoto,
discordImageStatus,
profileImageStatus,
) {
@@ -90,6 +100,7 @@ function createPhotoVerificationRequestButtonBox(
const approveBothButton = document.createElement('button');
approveBothButton.innerText = 'Approve Both';
+ approveBothButton.className = 'approve-both-button';
approveBothButton.onclick = () => approvePhoto('both');
buttonBox.appendChild(approveBothButton);
@@ -99,13 +110,19 @@ function createPhotoVerificationRequestButtonBox(
rejectButton.onclick = rejectPhoto;
buttonBox.appendChild(rejectButton);
+ const refreshDiscordAvatarButton = document.createElement('button');
+ refreshDiscordAvatarButton.innerText = 'Refresh Discord Image';
+ refreshDiscordAvatarButton.onclick = refreshDiscordAvatarPhoto;
+ refreshDiscordAvatarButton.className = 'refresh-discord-avatar-button';
+ buttonBox.appendChild(refreshDiscordAvatarButton);
+
const notifyButton = document.createElement('button');
notifyButton.innerText = 'Notify User';
notifyButton.onclick = notifyPhoto;
notifyButton.className = 'notify-button';
notifyButton.disabled = true;
-
buttonBox.appendChild(notifyButton);
+
return buttonBox;
}
@@ -169,6 +186,11 @@ function createPhotoVerificationRequestCard(photoVerificationRequest) {
photoVerificationRequest.userId,
),
() => notifyPhotoVerificationRequest(photoVerificationRequest.id),
+ () =>
+ refreshDiscordImage(
+ photoVerificationRequest.discordId,
+ photoVerificationRequest.id,
+ ),
photoVerificationRequest.discord.approved,
photoVerificationRequest.profile.approved,
),
@@ -207,6 +229,20 @@ async function modifyPhotoVerificationRequest(userId, imageType, status) {
return { data: await response.json(), statusCode: response.status };
}
+async function refreshDiscordImageRequest(discordId) {
+ const response = await fetch(
+ `${API_BASE_URL}/discord-actions/avatar/update/${discordId}`,
+ {
+ credentials: 'include',
+ method: 'PATCH',
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ },
+ );
+ return { data: await response.json(), statusCode: response.status };
+}
+
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
diff --git a/photo-verification-requests/style.css b/photo-verification-requests/style.css
index 90a412a3..107c4add 100644
--- a/photo-verification-requests/style.css
+++ b/photo-verification-requests/style.css
@@ -152,7 +152,8 @@
.photo-verification-button-box > .reject-button {
background-color: var(--color-red);
}
-.photo-verification-button-box > .notify-button {
+.photo-verification-button-box > .notify-button,
+.photo-verification-button-box > .refresh-discord-avatar-button {
background-color: var(--blue-color);
}
From bc87252d0d8129eebf6d168af8ce88a52e3a4e56 Mon Sep 17 00:00:00 2001
From: Vinayak Goyal <73058928+ivinayakg@users.noreply.github.com>
Date: Fri, 19 Apr 2024 23:28:42 +0530
Subject: [PATCH 5/5] refact: photo-verification refresh discord image link
---
__tests__/photo-verification/photo-verification.test.js | 2 +-
photo-verification-requests/local-utils.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/__tests__/photo-verification/photo-verification.test.js b/__tests__/photo-verification/photo-verification.test.js
index 109c5aea..507f59db 100644
--- a/__tests__/photo-verification/photo-verification.test.js
+++ b/__tests__/photo-verification/photo-verification.test.js
@@ -97,7 +97,7 @@ describe('Tests the Photo Verification Screen', () => {
});
} else if (
url ===
- `https://api.realdevsquad.com/discord-actions/avatar/update/${photoVerificationRequestsListPending.data[0].discordId}`
+ `https://api.realdevsquad.com/discord-actions/avatar/photo-verification-update/${photoVerificationRequestsListPending.data[0].discordId}`
) {
interceptedRequest.respond({
status: 200,
diff --git a/photo-verification-requests/local-utils.js b/photo-verification-requests/local-utils.js
index 492b9e65..fb4e1139 100644
--- a/photo-verification-requests/local-utils.js
+++ b/photo-verification-requests/local-utils.js
@@ -231,7 +231,7 @@ async function modifyPhotoVerificationRequest(userId, imageType, status) {
async function refreshDiscordImageRequest(discordId) {
const response = await fetch(
- `${API_BASE_URL}/discord-actions/avatar/update/${discordId}`,
+ `${API_BASE_URL}/discord-actions/avatar/photo-verification-update/${discordId}`,
{
credentials: 'include',
method: 'PATCH',