Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Div/appeals 63282 #23439

Open
wants to merge 4 commits into
base: feature/APPEALS-60433
Choose a base branch
from
Open
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
11 changes: 6 additions & 5 deletions app/controllers/correspondence_details_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ def access_redirect
def update_correspondence
if correspondence_intake_processor.update_correspondence(intake_processor_params)
render json: {
related_appeals: @correspondence.appeal_ids,
relatedAppeals: @correspondence.appeal_ids,
correspondence: serialized_correspondence,
correspondence_appeals: serialized_correspondence_appeals
correspondenceAppeals: serialized_correspondence_appeals
}, status: :created
else
render json: { error: "Failed to update records" }, status: :bad_request
Expand Down Expand Up @@ -168,8 +168,9 @@ def waive_evidence_submission_window_task
# returns all correspondence appeals tasks to be loaded into the redux store
def correspondences_appeals_tasks
tasks = []
@correspondence.correspondence_appeals.each do |cor_appeal|
tasks << appeals_tasks_for_frontend(cor_appeal)
@correspondence.correspondence_appeals&.each do |cor_appeal|
result = appeals_tasks_for_frontend(cor_appeal)
tasks << result if result.present?
end

render json: { tasks: json_appeal_tasks(tasks.flatten!) }
Expand Down Expand Up @@ -266,7 +267,7 @@ def appeals_tasks_for_frontend(cor_appeal)
def json_appeal_tasks(tasks, ama_serializer: WorkQueue::TaskSerializer)
AmaAndLegacyTaskSerializer.create_and_preload_legacy_appeals(
params: { user: current_user, role: "generic" },
tasks: tasks,
tasks: tasks || [],
ama_serializer: ama_serializer
).call
end
Expand Down
30 changes: 20 additions & 10 deletions app/models/concerns/decision_review_polymorphic_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@ module DecisionReviewPolymorphicHelper
def define_polymorphic_decision_review_associations(association_name, from_association_name, types = nil)
belongs_to association_name, polymorphic: true

# Specific association mappings that are uniquely different from the calculated class name to underscored symbol
association_name_mapping = { "Appeal" => :ama_appeal, "Hearing" => :ama_hearing, "Correspondence" => :correspondence }
scope_mapping = {
"Appeal" => :ama,
"LegacyAppeal" => :legacy,
"LegacyHearing" => :legacy,
"Hearing" => :ama,
"Correspondence" => :correspondence
}

association_name_mapping, scope_mapping = mapping_association_and_scope
# LegacyAppeals + all of the non abstract subtypes of DecisionReview not incuding child types for STI
types ||= %w[Appeal LegacyAppeal HigherLevelReview SupplementalClaim Correspondence]

Expand All @@ -32,5 +23,24 @@ def define_polymorphic_decision_review_associations(association_name, from_assoc
scope scope_name.to_sym, -> { where("#{association_name}_type": type) }
end
end

private

def mapping_accosiation_and_scope
# Specific association mappings that are uniquely different from the calculated class name to underscored symbol
association_name_mapping = {
"Appeal" => :ama_appeal,
"Hearing" => :ama_hearing,
"Correspondence" => :correspondence
}
scope_mapping = {
"Appeal" => :ama,
"LegacyAppeal" => :legacy,
"LegacyHearing" => :legacy,
"Hearing" => :ama,
"Correspondence" => :correspondence
}
[association_name_mapping, scope_mapping]
end
end
end
4 changes: 3 additions & 1 deletion client/app/components/Pagination/Pagination.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,19 @@ class Pagination extends React.PureComponent {
// If there are no pages, there is no data, so the range should be 0-0.
// Otherwise, the beginning of the range is the previous amount of cases + 1
let beginningCaseNumber;

if (searchValue) {
beginningCaseNumber = totalCases === 0 ? 0 : 1;
} else if (totalCases === 0) {
beginningCaseNumber = 0;
} else {
beginningCaseNumber = currentPage * pageSize - pageSize + 1;
beginningCaseNumber = (currentPage * pageSize) - pageSize + 1;
}
// If there are no pages, there is no data, so the range should be 0-0.
// Otherwise, the end of the range is the previous amount of cases +
// the amount of data in the current page.
let endingCaseNumber;

if (searchValue) {
endingCaseNumber = totalCases;
} else if (totalCases === 0) {
Expand Down
4 changes: 2 additions & 2 deletions client/app/queue/QueueTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -784,9 +784,9 @@ export default class QueueTable extends React.PureComponent {

if (enablePagination && !this.state.loadingComponent) {
if (searchValue) {
currentCases = totalTaskCount = this.filterTasksFromSearchbar(rowObjects, searchValue)?.length
currentCases = totalTaskCount = this.filterTasksFromSearchbar(rowObjects, searchValue)?.length;
} else {
currentCases = rowObjects ? rowObjects.length : 0
currentCases = rowObjects ? rowObjects.length : 0;
}
paginationElements = (
<Pagination
Expand Down
1 change: 0 additions & 1 deletion client/app/queue/components/TaskRows.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,6 @@ class TaskRows extends React.PureComponent {
eventsFromAppeal
);


return (
<React.Fragment key={appeal.externalId}>
{sortedTimelineEvents.map((timelineEvent, index) => {
Expand Down
91 changes: 37 additions & 54 deletions client/app/queue/correspondence/details/CorrespondenceDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import React, { useState, useEffect } from 'react';
import { useDispatch, connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { useHistory } from 'react-router';
import AppSegment from '@department-of-veterans-affairs/caseflow-frontend-toolkit/components/AppSegment';
import PropTypes from 'prop-types';
import TabWindow from '../../../components/TabWindow';
Expand Down Expand Up @@ -146,7 +145,7 @@ const CorrespondenceDetails = (props) => {

// Function to handle the "Save Changes" button click, including the PATCH and POST request
const handlepriorMailUpdate = async () => {
// Disable the button to prevent duplicate requests
// Disable the button to prevent duplicate requests
setDisableSubmitButton(true);

// Get the initial and current checkbox states
Expand Down Expand Up @@ -177,62 +176,45 @@ const CorrespondenceDetails = (props) => {
};

try {
// Helper function to check for success response
const isSuccess = (response) => response.ok;
// Helper function to check for success response
const isSuccess = (response) => response && response.ok;

// Send PATCH request to add checked relations if necessary
// If no checked items, POST is already successful
let patchSuccess = false;

const updateAppeals = (response) => {
const appealIds = response.body.related_appeals;

setSelectedAppeals(appealIds);
setInitialSelectedAppeals(appealIds);
sortAppeals(appealIds);
setAppealTableKey((key) => key + 1);
};

// Send POST request to create relations
const patchResponse = await ApiUtil.patch(
`/queue/correspondence/${correspondence.uuid}/update_correspondence`,
{ data: postData }
).
then((resp) => {
const appealIds = resp.body.related_appeals;
const correspondenceAppeals = resp.body.correspondence_appeals;
// Send a PATCH request to update correspondence with the prepared data
const patchResponse = await ApiUtil.patch(`/queue/correspondence/${correspondence.uuid}/update_correspondence`, {
data: postData
});

if (Object.keys(props.appealsFromStore).length < 1) {
return;
}
// Check if the PATCH request was successful
if (isSuccess(patchResponse)) {
const appealIds = patchResponse?.body?.relatedAppeals;
const correspondenceAppeals = patchResponse?.body?.correspondenceAppeals;

// Removes all entries in the queue.appeals redux store
Object.entries(props.appealsFromStore).forEach(
([, value]) => dispatch(deleteAppeal((value.externalId)))
);
if (appealIds && correspondenceAppeals) {
// Clear existing appeals from the Redux store
Object.entries(props.appealsFromStore).forEach(([, value]) => {
dispatch(deleteAppeal(value.externalId));
});

// Updates the queue.appeals redux store to match all correspondenceAppeals on Save
correspondenceAppeals.map((corAppeal) => {
// Fetch updated appeal details for the correspondence
correspondenceAppeals.forEach((corAppeal) => {
dispatch(fetchAppealDetails(corAppeal.appealUuid));
});

// Fetch tasks related to the correspondence
dispatch(fetchCorrespondencesAppealsTasks(correspondence.uuid));

// Update selected appeals in state
setSelectedAppeals(appealIds);
setInitialSelectedAppeals(appealIds);
sortAppeals(appealIds);
setAppealTableKey((key) => key + 1);

window.scrollTo({
top: 0,
behavior: 'smooth'
});
});

// Check for general success status (any 2xx status)
patchSuccess = isSuccess(patchResponse);
updateAppeals(patchResponse);
console.log('POST successful:', patchResponse.status); // eslint-disable-line no-console
// Smoothly scroll to the top of the page
window.scrollTo({ top: 0, behavior: 'smooth' });

// Only show success banner if both PATCH and POST requests succeeded
if (patchSuccess) {
setShowSuccessBanner(true);
// Show a success banner after the update
setShowSuccessBanner(true);
}
}

// Sort the prior mail into linked (checked) and unlinked (unchecked) groups
Expand All @@ -244,13 +226,13 @@ const CorrespondenceDetails = (props) => {
let sortOrder = 0;

if (firstInState && secondInState) {
// Sort linked mail from most recent to oldest
// Sort linked mail from most recent to oldest
sortOrder = new Date(second.vaDateOfReceipt) - new Date(first.vaDateOfReceipt);
} else if (!firstInState && !secondInState) {
// Sort unlinked mail from oldest to most recent
// Sort unlinked mail from oldest to most recent
sortOrder = new Date(first.vaDateOfReceipt) - new Date(second.vaDateOfReceipt);
} else if (firstInState) {
// Ensure linked items come before unlinked items
// Ensure linked items come before unlinked items
sortOrder = -1;
} else if (secondInState) {
sortOrder = 1;
Expand Down Expand Up @@ -474,7 +456,7 @@ const CorrespondenceDetails = (props) => {
return;
}

props.correspondence.correspondenceAppeals.map((corAppeal) => {
props.correspondence.correspondenceAppeals.forEach((corAppeal) => {
dispatch(fetchAppealDetails(corAppeal.appealUuid));
});

Expand Down Expand Up @@ -898,6 +880,7 @@ const CorrespondenceDetails = (props) => {
if (isAdminNotLoggedIn() === false) {
handlepriorMailUpdate();
} else if (selectedPriorMail.length > 0 || selectedAppeals.length > 0 || unSelectedAppeals.length > 0) {
setDisableSubmitButton(true);
const appealsSelected = selectedAppeals.filter((val) => !correspondence.correspondenceAppealIds.includes(val));
const priorMailIds = selectedPriorMail.map((mail) => mail.id);
const payload = {
Expand All @@ -914,8 +897,8 @@ const CorrespondenceDetails = (props) => {

return ApiUtil.patch(`/queue/correspondence/${correspondence.uuid}/update_correspondence`, payload).
then((resp) => {
const appealIds = resp.body.related_appeals;
const correspondenceAppeals = resp.body.correspondence_appeals;
const appealIds = resp.body.relatedAppeals;
const correspondenceAppeals = resp.body.correspondenceAppeals;

// Removes all entries in the queue.appeals redux store
Object.entries(props.appealsFromStore).forEach(
Expand All @@ -924,7 +907,7 @@ const CorrespondenceDetails = (props) => {

// Updates the queue.appeals redux store to match all correspondenceAppeals on Save

correspondenceAppeals.map((corAppeal) => {
correspondenceAppeals.forEach((corAppeal) => {
dispatch(fetchAppealDetails(corAppeal.appealUuid));
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ const AddTaskModalCorrespondenceDetails = ({
// Filters task type options based on unrelated tasks, excluding already selected tasks
const getFilteredTaskTypeOptions = () => {
return INTAKE_FORM_TASK_TYPES.unrelatedToAppeal.
filter((option) => !unrelatedTaskList.some((existingTask) => existingTask.label.toLowerCase() === option.label.toLowerCase())).
filter((option) =>
!unrelatedTaskList.some(
(existingTask) => existingTask.label.toLowerCase() === option.label.toLowerCase()
)
).
map((option) => ({ value: option.value, label: option.label }));
};

Expand All @@ -52,7 +56,8 @@ const AddTaskModalCorrespondenceDetails = ({
setTaskTypeOptions(getFilteredTaskTypeOptions());
}, [unrelatedTaskList]);

// Submit disabled if task type is not selected, the page is loading, or task content and additional are not filled out
// Submit disabled if task type is not selected,
// the page is loading, or task content and additional are not filled out
const isSubmitDisabled = !(taskContent || additionalContent) || !selectedTaskType || isLoading;

// Handle task type selection, stores the selected task type
Expand Down Expand Up @@ -123,6 +128,14 @@ const AddTaskModalCorrespondenceDetails = ({
return null;
}

let buttonText = 'Next';

if (isLoading) {
buttonText = 'Loading...';
} else if (isSecondPage) {
buttonText = 'Submit';
}

return (
<Modal
// Dynamic title for each page
Expand All @@ -134,7 +147,7 @@ const AddTaskModalCorrespondenceDetails = ({
onClick={isSecondPage ? handleSubmit : handleNext}
disabled={isSecondPage ? isSubmitDisabled : false}
>
{isLoading ? 'Loading...' : isSecondPage ? 'Submit' : 'Next'}
{buttonText}
</Button>
}
cancelButton={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,17 @@
end
expect(page).not_to have_selector("#submit-correspondence-intake-modal", visible: false)
end

it "validating the Instructional text update on Linked Appeals Gray Table" do
existing_apppeals_list(@correspondence)
all(".plus-symbol")[0].click
page.all(".cf-form-checkbox")[1].click
expect(page).to have_content("The linked appeal must be saved before tasks can be added.")
click_button "Save changes"
using_wait_time(wait_time) do
expect(page).to have_content("You have successfully saved changes to this page")
expect(page).not_to have_content("The linked appeal must be saved before tasks can be added.")
end
end
end
end
Loading