Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 82 additions & 3 deletions __tests__/applications/applications.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,26 @@ describe('Applications page', () => {
});
} else if (
url === `${STAGING_API_URL}/applications/lavEduxsb2C5Bl4s289P`
) {
interceptedRequest.respond({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ application: pendingApplications[0] }),
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
} else if (
interceptedRequest.method() === 'PATCH' &&
url === `${STAGING_API_URL}/applications/lavEduxsb2C5Bl4s289P/feedback`
) {
interceptedRequest.respond({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
message: 'application updated successfully!',
message: 'Application feedback submitted successfully',
}),
headers: {
'Access-Control-Allow-Origin': '*',
Expand Down Expand Up @@ -210,7 +224,9 @@ describe('Applications page', () => {
element.scrollIntoView({ behavior: 'auto' });
}
});
await page.waitForNetworkIdle();
await page.waitForFunction(
() => document.querySelectorAll('.application-card').length >= 12,
);
applicationCards = await page.$$('.application-card');
expect(applicationCards.length).toBe(12);
});
Expand Down Expand Up @@ -355,7 +371,7 @@ describe('Applications page', () => {
).toBe(false);
const toastMessage = await page.$('[data-testid="toast-message"]');
expect(await toastMessage.evaluate((el) => el.textContent)).toBe(
'application updated successfully!',
'Application feedback submitted successfully',
);
});

Expand All @@ -371,4 +387,67 @@ describe('Applications page', () => {
const applicationCardElements = await page.$$('.application-card');
expect(applicationCardElements.length).toBe(acceptedApplications.length);
});

it('should show Request changes button when application details modal is open for a pending application', async () => {
await page.goto(
`${LOCAL_TEST_PAGE_URL}/applications?dev=true&status=pending`,
);
await page.waitForSelector('.application-card');
await page.click('.application-card');
await page.waitForSelector('.application-details:not(.hidden)');
const requestChangesButton = await page.$(
'.application-details-request-changes',
);
expect(requestChangesButton).toBeTruthy();
const isHidden = await requestChangesButton.evaluate((el) =>
el.classList.contains('hidden'),
);
expect(isHidden).toBe(false);
});

it('should show error toast when clicking Request changes without feedback text', async () => {
await page.goto(
`${LOCAL_TEST_PAGE_URL}/applications?dev=true&status=pending`,
);
await page.waitForSelector('.application-card');
await page.click('.application-card');
await page.waitForSelector('.application-details:not(.hidden)');
await page.click('.application-details-request-changes');
await page.waitForSelector('[data-testid="toast-component"].show');
const toastComponent = await page.$('[data-testid="toast-component"]');
expect(
await toastComponent.evaluate((el) =>
el.classList.contains('error__toast'),
),
).toBe(true);
const toastMessage = await page.$('[data-testid="toast-message"]');
expect(await toastMessage.evaluate((el) => el.textContent)).toBe(
'Feedback is required when requesting changes.',
);
});

it('should show success toast when submitting Request changes with feedback text', async () => {
await page.goto(
`${LOCAL_TEST_PAGE_URL}/applications?dev=true&status=pending`,
);
await page.waitForSelector('.application-card');
await page.click('.application-card');
await page.waitForSelector('.application-details:not(.hidden)');
await page.type(
'.application-textarea',
'Please add more details on skills',
);
await page.click('.application-details-request-changes');
await page.waitForSelector('[data-testid="toast-component"].show');
const toastComponent = await page.$('[data-testid="toast-component"]');
expect(
await toastComponent.evaluate((el) =>
el.classList.contains('success__toast'),
),
).toBe(true);
const toastMessage = await page.$('[data-testid="toast-message"]');
expect(await toastMessage.evaluate((el) => el.textContent)).toBe(
'Application feedback submitted successfully',
);
});
});
7 changes: 7 additions & 0 deletions applications/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ <h2>Status</h2>
>
Accept
</button>
<button
id="application-details-request-changes"
class="application-details-request-changes"
aria-label="Request Application Changes"
>
Request changes
</button>
<button
class="application-details-reject"
aria-label="Reject Application"
Expand Down
114 changes: 84 additions & 30 deletions applications/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
getApplications,
getIsSuperUser,
showToast,
updateApplication,
submitApplicationFeedback,
getApplicationById,
} from './utils.js';
let nextLink;
Expand Down Expand Up @@ -32,6 +32,9 @@ const applicationAcceptButton = document.querySelector(
const applicationRejectButton = document.querySelector(
'.application-details-reject',
);
const applicationRequestChangesButton = document.getElementById(
'application-details-request-changes',
);
const applyFilterButton = document.getElementById('apply-filter-button');
const applicationContainer = document.querySelector('.application-container');
const clearButton = document.getElementById('clear-button');
Expand Down Expand Up @@ -81,27 +84,26 @@ let currentApplicationId;

let status = 'all';

function updateUserApplication({ isAccepted }) {
function updateUserApplication({ status: actionStatus }) {
const applicationTextarea = document.querySelector('.application-textarea');
let status;
const payload = {};

if (isAccepted) status = 'accepted';
else status = 'rejected';

payload['status'] = status;

if (applicationTextarea.value) {
payload.feedback = applicationTextarea.value;
const feedbackText = (applicationTextarea?.value ?? '').trim();

if (actionStatus === 'changes_requested' && !feedbackText) {
showToastMessage({
isDev,
oldToastFunction: showToast,
type: 'error',
message: 'Feedback is required when requesting changes.',
});
return;
}

updateApplication({
submitApplicationFeedback({
applicationId: currentApplicationId,
applicationPayload: payload,
status: actionStatus,
feedback: feedbackText || undefined,
})
.then((res) => {
const updatedFeedback = payload.feedback || '';
applicationTextarea.value = updatedFeedback;
showToastMessage({
isDev,
oldToastFunction: showToast,
Expand All @@ -115,7 +117,7 @@ function updateUserApplication({ isAccepted }) {
isDev,
oldToastFunction: showToast,
type: 'error',
message: error.message,
message: error.message || 'Failed to submit feedback.',
});
});
}
Expand Down Expand Up @@ -144,12 +146,18 @@ function closeApplicationDetails() {
const applicationRejectedMsg = document.querySelector(
'.application-details-rejected-msg',
);
const applicationChangesMsg = document.querySelector(
'.application-details-changes-msg',
);
if (applicationAcceptedMsg) {
applicationAcceptedMsg.remove();
}
if (applicationRejectedMsg) {
applicationRejectedMsg.remove();
}
if (applicationChangesMsg) {
applicationChangesMsg.remove();
}
removeQueryParamInUrl('id');
}

Expand All @@ -167,6 +175,14 @@ function openApplicationDetails(application) {
title: 'Status',
description: application.status,
},
{
title: 'Score',
description: application.score ?? 'N/A',
},
{
title: 'Nudge Count',
description: application.nudgeCount ?? 'N/A',
},
{
title: 'Introduction',
description: application.intro.introduction,
Expand Down Expand Up @@ -220,11 +236,27 @@ function openApplicationDetails(application) {
attributes: { class: 'section-title' },
innerText: application.title,
});
const applicationSectionDescription = createElement({
type: 'p',
attributes: { class: 'description' },
innerText: application.description,
});

let applicationSectionDescription;
if (application.title === 'Status') {
const statusLabel = application.description
.split('_')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
applicationSectionDescription = createElement({
type: 'span',
attributes: {
class: `status-badge status-badge--${application.description}`,
},
innerText: statusLabel,
});
} else {
applicationSectionDescription = createElement({
type: 'p',
attributes: { class: 'description' },
innerText: application.description,
});
}

applicationSection.appendChild(applicationSectionTitle);
applicationSection.appendChild(applicationSectionDescription);
Expand All @@ -246,17 +278,20 @@ function openApplicationDetails(application) {
type: 'textarea',
attributes: {
class: 'application-textarea',
placeHolder: 'Add Feedback here',
placeholder: 'Add Feedback here (required for Request changes)',
},
innerText: application.feedback || '',
innerText: '',
});

applicationSection.appendChild(applicationSectionTitle);
applicationSection.appendChild(applicationTextArea);
applicationDetailsMain.appendChild(applicationSection);
if (application.status === 'pending') {
applicationSection.appendChild(applicationSectionTitle);
applicationSection.appendChild(applicationTextArea);
applicationDetailsMain.appendChild(applicationSection);
}

if (application.status === 'rejected') {
applicationAcceptButton.classList.add('hidden');
applicationRequestChangesButton.classList.add('hidden');
applicationRejectButton.classList.add('hidden');
const applicationDetailsRejectedMsg = createElement({
type: 'p',
Expand All @@ -268,6 +303,7 @@ function openApplicationDetails(application) {
applicationDetailsActionsContainer.append(applicationDetailsRejectedMsg);
} else if (application.status === 'accepted') {
applicationAcceptButton.classList.add('hidden');
applicationRequestChangesButton.classList.add('hidden');
applicationRejectButton.classList.add('hidden');
const applicationDetailsAcceptedMsg = createElement({
type: 'p',
Expand All @@ -277,12 +313,27 @@ function openApplicationDetails(application) {
innerText: 'Application was already accepted',
});
applicationDetailsActionsContainer.append(applicationDetailsAcceptedMsg);
} else if (application.status === 'changes_requested') {
applicationAcceptButton.classList.add('hidden');
applicationRequestChangesButton.classList.add('hidden');
applicationRejectButton.classList.add('hidden');
const applicationDetailsChangesMsg = createElement({
type: 'p',
attributes: {
class: 'application-details-changes-msg',
},
innerText: 'Changes have been requested for this application',
});
applicationDetailsActionsContainer.append(applicationDetailsChangesMsg);
} else {
applicationRejectButton.disabled = false;
applicationRejectButton.style.cursor = 'pointer';
applicationRejectButton.classList.remove('disable-button');
applicationRejectButton.classList.remove('hidden');

applicationRequestChangesButton.disabled = false;
applicationRequestChangesButton.style.cursor = 'pointer';
applicationRequestChangesButton.classList.remove('disable-button');
applicationRequestChangesButton.classList.remove('hidden');
applicationAcceptButton.classList.remove('hidden');
applicationAcceptButton.disabled = false;
applicationAcceptButton.style.cursor = 'pointer';
Expand Down Expand Up @@ -635,8 +686,11 @@ closeDropdownBtn.addEventListener('click', () => {
});

applicationAcceptButton.addEventListener('click', () =>
updateUserApplication({ isAccepted: true }),
updateUserApplication({ status: 'accepted' }),
);
applicationRequestChangesButton.addEventListener('click', () =>
updateUserApplication({ status: 'changes_requested' }),
);
applicationRejectButton.addEventListener('click', () =>
updateUserApplication({ isAccepted: false }),
updateUserApplication({ status: 'rejected' }),
);
Loading
Loading