Skip to content

Commit 05b8ebb

Browse files
authored
Merge pull request #795 from Real-Dev-Squad/develop
Dev to Main sync
2 parents 2f81d3e + 92fd285 commit 05b8ebb

File tree

8 files changed

+171
-40
lines changed

8 files changed

+171
-40
lines changed

__tests__/applications/applications.test.js

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@ describe('Applications page', () => {
3030

3131
page.on('request', (request) => {
3232
if (
33-
request.url() === `${API_BASE_URL}/applications?size=5` ||
33+
request.url() === `${API_BASE_URL}/applications?size=6` ||
3434
request.url() ===
35-
`${API_BASE_URL}/applications?next=YwTi6zFNI3GlDsZVjD8C&size=5`
35+
`${API_BASE_URL}/applications?next=YwTi6zFNI3GlDsZVjD8C&size=6`
3636
) {
3737
request.respond({
3838
status: 200,
3939
contentType: 'application/json',
4040
body: JSON.stringify({
4141
applications: fetchedApplications,
42-
next: '/applications?next=YwTi6zFNI3GlDsZVjD8C&size=5',
42+
next: '/applications?next=YwTi6zFNI3GlDsZVjD8C&size=6',
4343
}),
4444
headers: {
4545
'Access-Control-Allow-Origin': '*',
@@ -48,7 +48,7 @@ describe('Applications page', () => {
4848
},
4949
});
5050
} else if (
51-
request.url() === `${API_BASE_URL}/applications?size=5&status=accepted`
51+
request.url() === `${API_BASE_URL}/applications?size=6&status=accepted`
5252
) {
5353
request.respond({
5454
status: 200,
@@ -72,7 +72,7 @@ describe('Applications page', () => {
7272
body: JSON.stringify(superUserForAudiLogs),
7373
});
7474
} else if (
75-
request.url() === `${API_BASE_URL}/applications/lavEduxsb2C5Bl4s289P`
75+
request.url() === `${API_BASE_URL}/applications/lavEduxsb2C6Bl4s289P`
7676
) {
7777
request.respond({
7878
status: 200,
@@ -109,7 +109,7 @@ describe('Applications page', () => {
109109
expect(title).toBeTruthy();
110110
expect(filterButton).toBeTruthy();
111111
expect(applicationCards).toBeTruthy();
112-
expect(applicationCards.length).toBe(5);
112+
expect(applicationCards.length).toBe(6);
113113
});
114114

115115
it('should load and render the accepted application requests when accept is selected from filter, and after clearing the filter it should again show all the applications', async function () {
@@ -128,12 +128,16 @@ describe('Applications page', () => {
128128

129129
await page.waitForNetworkIdle();
130130
applicationCards = await page.$$('.application-card');
131-
expect(applicationCards.length).toBe(5);
131+
expect(applicationCards.length).toBe(6);
132+
const urlAfterClearingStatusFilter = new URL(page.url());
133+
expect(
134+
urlAfterClearingStatusFilter.searchParams.get('status') === null,
135+
).toBe(true, 'status query param is not removed from url');
132136
});
133137

134138
it('should load more applications on going to the bottom of the page', async function () {
135139
let applicationCards = await page.$$('.application-card');
136-
expect(applicationCards.length).toBe(5);
140+
expect(applicationCards.length).toBe(6);
137141
await page.evaluate(() => {
138142
const element = document.querySelector('#page_bottom_element');
139143
if (element) {
@@ -142,7 +146,7 @@ describe('Applications page', () => {
142146
});
143147
await page.waitForNetworkIdle();
144148
applicationCards = await page.$$('.application-card');
145-
expect(applicationCards.length).toBe(10);
149+
expect(applicationCards.length).toBe(12);
146150
});
147151

148152
it('should open application details modal for application, when user click on view details on any card', async function () {
@@ -158,9 +162,42 @@ describe('Applications page', () => {
158162
el.classList.contains('hidden'),
159163
),
160164
).toBe(false);
165+
const urlAfterOpeningModal = new URL(page.url());
166+
expect(urlAfterOpeningModal.searchParams.get('id') !== null).toBe(true);
167+
});
168+
169+
it('should close application details modal, when user clicks the close button', async function () {
170+
const applicationDetailsModal = await page.$('.application-details');
171+
await page.click('.view-details-button');
172+
await applicationDetailsModal.$eval('.application-close-button', (node) =>
173+
node.click(),
174+
);
175+
expect(
176+
await applicationDetailsModal.evaluate((el) =>
177+
el.classList.contains('hidden'),
178+
),
179+
).toBe(true);
180+
const urlAfterClosingModal = new URL(page.url());
181+
expect(urlAfterClosingModal.searchParams.get('id') === null).toBe(
182+
true,
183+
'id query param is not removed from url',
184+
);
185+
});
186+
187+
it('should load all applications behind the modal on applications/?id= page load', async function () {
188+
await page.click('.view-details-button');
189+
await page.reload();
190+
await page.waitForNetworkIdle();
191+
const applicationDetailsModal = await page.$('.application-details');
192+
await applicationDetailsModal.$eval('.application-close-button', (node) =>
193+
node.click(),
194+
);
195+
const applicationCards = await page.$$('.application-card');
196+
expect(applicationCards).toBeTruthy();
197+
expect(applicationCards.length).toBe(6);
161198
});
162199

163-
it('should show toast message with application updated successfully', async function () {
200+
it.skip('should show toast message with application updated successfully', async function () {
164201
await page.click('.view-details-button');
165202
await page.click('.application-details-accept');
166203
const toast = await page.$('#toast');

applications/script.js

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ const applyFilterButton = document.getElementById('apply-filter-button');
3232
const applicationContainer = document.querySelector('.application-container');
3333
const clearButton = document.getElementById('clear-button');
3434
const lastElementContainer = document.getElementById('page_bottom_element');
35+
36+
const urlParams = new URLSearchParams(window.location.search);
37+
let applicationId = urlParams.get('id');
38+
3539
let currentApplicationId;
3640

3741
let status = 'all';
@@ -46,18 +50,22 @@ function updateUserApplication({ isAccepted }) {
4650

4751
payload['status'] = status;
4852

49-
if (applicationTextarea.value) payload.feedback = applicationTextarea.value;
53+
if (applicationTextarea.value) {
54+
payload.feedback = applicationTextarea.value;
55+
}
5056

5157
updateApplication({
5258
applicationId: currentApplicationId,
5359
applicationPayload: payload,
5460
})
5561
.then((res) => {
56-
closeApplicationDetails();
62+
const updatedFeedback = payload.feedback || '';
63+
applicationTextarea.value = updatedFeedback;
64+
5765
showToast({ type: 'success', message: res.message });
66+
setTimeout(() => closeApplicationDetails(), 1000);
5867
})
5968
.catch((error) => {
60-
closeApplicationDetails();
6169
showToast({ type: 'error', message: error.message });
6270
});
6371
}
@@ -73,6 +81,7 @@ function closeApplicationDetails() {
7381
applicationDetailsModal.classList.add('hidden');
7482
backDropBlur.style.display = 'none';
7583
document.body.style.overflow = 'auto';
84+
removeQueryParamInUrl('id');
7685
}
7786

7887
function openApplicationDetails(application) {
@@ -170,15 +179,35 @@ function openApplicationDetails(application) {
170179
class: 'application-textarea',
171180
placeHolder: 'Add Feedback here',
172181
},
182+
innerText: application.feedback || '',
173183
});
174184

175185
applicationSection.appendChild(applicationSectionTitle);
176186
applicationSection.appendChild(applicationTextArea);
177187
applicationDetailsMain.appendChild(applicationSection);
188+
189+
if (application.status === 'rejected') {
190+
applicationRejectButton.disabled = true;
191+
applicationRejectButton.style.cursor = 'not-allowed';
192+
applicationRejectButton.classList.add('disable-button');
193+
} else if (application.status === 'accepted') {
194+
applicationAcceptButton.disabled = true;
195+
applicationAcceptButton.style.cursor = 'not-allowed';
196+
applicationAcceptButton.classList.add('disable-button');
197+
} else {
198+
applicationRejectButton.disabled = false;
199+
applicationRejectButton.style.cursor = 'pointer';
200+
applicationRejectButton.classList.remove('disable-button');
201+
202+
applicationAcceptButton.disabled = false;
203+
applicationAcceptButton.style.cursor = 'pointer';
204+
applicationAcceptButton.classList.remove('disable-button');
205+
}
178206
}
179207

180208
function clearFilter() {
181209
if (status === 'all') return;
210+
removeQueryParamInUrl('status');
182211
changeFilter();
183212
const selectedFilterOption = document.querySelector(
184213
'input[name="status"]:checked',
@@ -193,6 +222,23 @@ function changeLoaderVisibility({ hide }) {
193222
else loader.classList.remove('hidden');
194223
}
195224

225+
function addQueryParamInUrl(queryParamKey, queryParamVal) {
226+
const currentUrlParams = new URLSearchParams(window.location.search);
227+
currentUrlParams.append(queryParamKey, queryParamVal);
228+
const updatedUrl = '/applications/?' + currentUrlParams.toString();
229+
window.history.replaceState(window.history.state, '', updatedUrl);
230+
}
231+
232+
function removeQueryParamInUrl(queryParamKey) {
233+
const currentUrlParams = new URLSearchParams(window.location.search);
234+
currentUrlParams.delete(queryParamKey);
235+
let updatedUrl = '/applications/';
236+
if (currentUrlParams.size > 0) {
237+
updatedUrl += '?' + currentUrlParams.toString();
238+
}
239+
window.history.replaceState(window.history.state, '', updatedUrl);
240+
}
241+
196242
function createApplicationCard({ application }) {
197243
const applicationCard = createElement({
198244
type: 'div',
@@ -212,13 +258,13 @@ function createApplicationCard({ application }) {
212258

213259
const companyNameText = createElement({
214260
type: 'p',
215-
attributes: { class: 'company-name' },
261+
attributes: { class: 'company-name hide-overflow' },
216262
innerText: `Company name: ${application.professional.institution}`,
217263
});
218264

219265
const skillsText = createElement({
220266
type: 'p',
221-
attributes: { class: 'skills' },
267+
attributes: { class: 'skills hide-overflow' },
222268
innerText: `Skills: ${application.professional.skills}`,
223269
});
224270

@@ -228,7 +274,7 @@ function createApplicationCard({ application }) {
228274

229275
const introductionText = createElement({
230276
type: 'p',
231-
attributes: { class: 'user-intro' },
277+
attributes: { class: 'user-intro hide-overflow' },
232278
innerText: application.intro.introduction.slice(0, 200),
233279
});
234280

@@ -238,9 +284,10 @@ function createApplicationCard({ application }) {
238284
innerText: 'View Details',
239285
});
240286

241-
viewDetailsButton.addEventListener('click', () =>
242-
openApplicationDetails(application),
243-
);
287+
viewDetailsButton.addEventListener('click', () => {
288+
addQueryParamInUrl('id', application.id);
289+
openApplicationDetails(application);
290+
});
244291

245292
applicationCard.appendChild(userInfoContainer);
246293
applicationCard.appendChild(introductionText);
@@ -283,10 +330,7 @@ async function renderApplicationById(id) {
283330
if (!application) {
284331
return noApplicationFoundText.classList.remove('hidden');
285332
}
286-
287-
const applicationCard = createApplicationCard({ application });
288-
applicationContainer.appendChild(applicationCard);
289-
applicationContainer.classList.add('center');
333+
openApplicationDetails(application);
290334
} catch (error) {
291335
console.error('Error fetching application by user ID:', error);
292336
noApplicationFoundText.classList.remove('hidden');
@@ -310,17 +354,18 @@ async function renderApplicationById(id) {
310354
changeLoaderVisibility({ hide: true });
311355
return;
312356
}
357+
const urlParams = new URLSearchParams(window.location.search);
358+
status = urlParams.get('status') || 'all';
313359

314-
const queryString = window.location.search;
315-
const urlParams = new URLSearchParams(queryString);
316-
const applicationId = urlParams.get('id');
360+
if (status !== 'all') {
361+
document.querySelector(`input[name="status"]#${status}`).checked = true;
362+
}
317363

318364
if (applicationId) {
319365
await renderApplicationById(applicationId);
320-
} else {
321-
await renderApplicationCards('', status, true);
322-
addIntersectionObserver();
323366
}
367+
await renderApplicationCards('', status, true, applicationId);
368+
addIntersectionObserver();
324369

325370
changeLoaderVisibility({ hide: true });
326371
})();
@@ -355,8 +400,11 @@ applyFilterButton.addEventListener('click', () => {
355400
const selectedFilterOption = document.querySelector(
356401
'input[name="status"]:checked',
357402
);
403+
404+
const selectedStatus = selectedFilterOption.value;
405+
addQueryParamInUrl('status', selectedStatus);
358406
changeFilter();
359-
status = selectedFilterOption.value;
407+
status = selectedStatus;
360408
renderApplicationCards(nextLink, status);
361409
});
362410

applications/style.css

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ body {
147147
flex-wrap: wrap;
148148
justify-content: space-between;
149149
padding-bottom: 10px;
150+
padding-top: 32px;
150151
gap: 25px;
151152
}
152153

@@ -157,7 +158,7 @@ body {
157158
.application-card {
158159
border-radius: 15px;
159160
box-shadow: var(--elevation-1);
160-
padding: 15px;
161+
padding: 24px;
161162
width: 44%;
162163
display: flex;
163164
flex-direction: column;
@@ -186,6 +187,7 @@ body {
186187

187188
font-weight: 700;
188189
line-height: normal;
190+
padding-bottom: 8px;
189191
}
190192

191193
.application-card .user-info .company-name {
@@ -202,6 +204,12 @@ body {
202204
line-height: normal;
203205
}
204206

207+
.hide-overflow {
208+
white-space: nowrap;
209+
overflow: hidden;
210+
text-overflow: ellipsis;
211+
}
212+
205213
.application-card .user-intro {
206214
color: var(--color-gray);
207215
font-size: 16px;
@@ -234,6 +242,10 @@ body {
234242
margin: 30px;
235243
}
236244

245+
.loader.hidden {
246+
display: none;
247+
}
248+
237249
#page_bottom_element {
238250
width: 100%;
239251
height: 20px;
@@ -269,7 +281,7 @@ body {
269281
.application-details .application-details-main {
270282
height: 90%;
271283
overflow-y: auto;
272-
padding: 15px;
284+
padding: 15px 30px;
273285
display: flex;
274286
flex-direction: column;
275287
gap: 25px;
@@ -368,6 +380,10 @@ body {
368380
font-weight: 600;
369381
}
370382

383+
.no_applications_found.hidden {
384+
display: none;
385+
}
386+
371387
.close-button-icon {
372388
width: 32px;
373389
height: 32px;
@@ -395,6 +411,10 @@ body {
395411
background: var(--color-red-variant1);
396412
}
397413

414+
.disable-button {
415+
opacity: 0.2;
416+
}
417+
398418
@keyframes slideIn {
399419
from {
400420
right: -300px;

0 commit comments

Comments
 (0)