src/applications/claims-status/utils/appeals-v2-helpers.jsx
import React from 'react';
import moment from 'moment';
import { find, get, startCase } from 'lodash';
import * as Sentry from '@sentry/browser';
import { Link } from 'react-router-dom-v5-compat';
import { Toggler } from '~/platform/utilities/feature-toggles';
import Decision from '../components/appeals-v2/Decision';
import { ITEMS_PER_PAGE } from '../constants';
// This literally determines how many rows are displayed per page on the v2 index page
export const DECISION_REVIEW_URL = '/decision-reviews';
export const APPEAL_ACTIONS = {
original: 'original',
postRemand: 'post_remand',
postCavcRemand: 'post_cavc_remand',
reconsideration: 'reconsideration',
cue: 'cue',
other: 'other',
};
export const APPEAL_TYPES = {
legacy: 'legacyAppeal',
supplementalClaim: 'supplementalClaim',
higherLevelReview: 'higherLevelReview',
appeal: 'appeal',
};
export const appealTypes = Object.values(APPEAL_TYPES);
export const programAreaMap = {
compensation: 'disability compensation',
pension: 'pension',
insurance: 'insurance',
loan_guaranty: 'loan guaranty', // eslint-disable-line camelcase
education: 'education',
vre: 'vocational rehabilitation and employment',
medical: 'health care',
burial: 'burial benefits',
fiduciary: 'fiduciary',
};
/**
* Returns a string with the formatted name of the type of appeal.
* @param {Object} appeal
* @returns {string}
*/
export function getTypeName(appeal) {
switch (appeal.type) {
case APPEAL_TYPES.supplementalClaim:
return 'supplemental claim';
case APPEAL_TYPES.higherLevelReview:
return 'higher-level review';
case APPEAL_TYPES.legacy:
case APPEAL_TYPES.appeal:
return 'appeal';
default:
Sentry.withScope(scope => {
scope.setExtra('type', appeal.type);
Sentry.captureMessage('appeals-unknown-type');
});
return null;
}
}
export const DOCKET_TYPES = {
directReview: 'directReview',
evidenceSubmission: 'evidenceSubmission',
hearingRequest: 'hearingRequest',
};
/**
* Returns a string with the formatted name of the AMA docket.
* @param {string} docket
* @returns {string}
*/
export function getDocketName(docket) {
switch (docket) {
case DOCKET_TYPES.directReview:
return 'Direct Review';
case DOCKET_TYPES.evidenceSubmission:
return 'Evidence Submission';
case DOCKET_TYPES.hearingRequest:
return 'Hearing Request';
default:
return docket;
}
}
export const AOJS = {
vba: 'vba',
vha: 'vha',
nca: 'nca',
other: 'other',
};
export function getAojDescription(aoj) {
switch (aoj) {
case AOJS.vba:
return 'Veterans Benefits Administration';
case AOJS.vha:
return 'Veterans Health Administration';
case AOJS.nca:
return 'National Cemetery Administration';
default:
return 'Agency of Original Jurisdiction';
}
}
// TO DO: Replace these properties and content with real versions once finalized.
export const STATUS_TYPES = {
amaRemand: 'ama_remand',
atVso: 'at_vso',
bvaDecision: 'bva_decision',
bvaDecisionEffectuation: 'bva_decision_effectuation',
bvaDevelopment: 'bva_development',
death: 'death',
decisionInProgress: 'decision_in_progress',
evidentiaryPeriod: 'evidentiary_period',
fieldGrant: 'field_grant',
ftr: 'ftr',
hlrClosed: 'hlr_closed',
hlrDtaError: 'hlr_dta_error',
hlrDecision: 'hlr_decision',
hlrReceived: 'hlr_received',
merged: 'merged',
onDocket: 'on_docket',
otherClose: 'other_close',
pendingCertification: 'pending_certification',
pendingCertificationSsoc: 'pending_certification_ssoc',
pendingForm9: 'pending_form9',
pendingHearingScheduling: 'pending_hearing_scheduling',
pendingSoc: 'pending_soc',
postBvaDtaDecision: 'post_bva_dta_decision',
ramp: 'ramp',
reconsideration: 'reconsideration',
remand: 'remand',
remandReturn: 'remand_return',
remandSsoc: 'remand_ssoc',
scClosed: 'sc_closed',
scDecision: 'sc_decision',
scReceived: 'sc_received',
scheduledHearing: 'scheduled_hearing',
statutoryOptIn: 'statutory_opt_in',
stayed: 'stayed',
withdrawn: 'withdrawn',
};
export const ISSUE_STATUS = {
fieldGrant: 'field_grant',
withdrawn: 'withdrawn',
allowed: 'allowed',
denied: 'denied',
remand: 'remand',
cavcRemand: 'cavc_remand',
};
export const claimsAvailability = {
AVAILABLE: 'AVAILABLE',
UNAVAILABLE: 'UNAVAILABLE',
};
// TO-DO: Ensure availability refs point to this instead of the actions above
export const appealsAvailability = {
USER_FORBIDDEN_ERROR: 'USER_FORBIDDEN_ERROR',
RECORD_NOT_FOUND_ERROR: 'RECORD_NOT_FOUND_ERROR',
VALIDATION_ERROR: 'VALIDATION_ERROR',
BACKEND_SERVICE_ERROR: 'BACKEND_SERVICE_ERROR',
FETCH_APPEALS_ERROR: 'FETCH_APPEALS_ERROR',
AVAILABLE: 'AVAILABLE',
};
export const ALERT_TYPES = {
form9Needed: 'form9_needed',
scheduledHearing: 'scheduled_hearing',
hearingNoShow: 'hearing_no_show',
heldForEvidence: 'held_for_evidence',
cavcOption: 'cavc_option',
rampEligible: 'ramp_eligible',
rampIneligible: 'ramp_ineligible',
decisionSoon: 'decision_soon',
blockedByVso: 'blocked_by_vso',
evidentiaryPeriod: 'evidentiary_period',
amaPostDecision: 'ama_post_decision',
};
/**
* Takes an array of appeals and returns another array of issue descriptions
* and where in the appeal lifecycle each issue is (open, remand, granted, denied)
* @typedef {Object} issue an individual issue - many issues can be a part of a single appeal
* @property {bool} active indicates whether an appeal is open or closed
* @property {string} description more info about the specific injury in the issue
* @property {string} diagnosticCode a codified version of the description
* @property {('field_grant'|'withdrawn'|'allowed'|'denied'|'remand'|'cavc_remand')} lastAction
* @property {string} date TO-DO: unsure of what this date siginifies
* ------------------------------------------------------------------------------------------------
* @typedef {Object} segmentedIssue issue with descriptor and status information
* @property {('granted'|'remand'|'allowed'|'denied'|'withdrawn')} status lifecycle stage of an issue
* @property {string} description pass-through for the description info of passed in issue object
* ------------------------------------------------------------------------------------------------
* @param {issue[]} issues all the individual issues that are attached to an appeal
* @returns {segmentedIssue[]} an array of issue objects with statuses and descriptions
*/
export function addStatusToIssues(issues) {
return issues.map(issue => {
let status = '';
switch (issue.lastAction) {
case ISSUE_STATUS.fieldGrant:
status = 'granted';
break;
case ISSUE_STATUS.withdrawn:
status = 'withdrawn';
break;
case ISSUE_STATUS.allowed:
status = 'granted';
break;
case ISSUE_STATUS.denied:
status = 'denied';
break;
case ISSUE_STATUS.remand:
status = 'remand';
break;
case ISSUE_STATUS.cavcRemand:
status = 'remand';
break;
default:
// if an issue's lastAction isn't one of the above, it's null,
// which signifies that it's still open
status = 'open';
break;
}
return { status, description: issue.description };
});
}
/**
* Finds an appeal from the Redux store with ID matching arg ID.
* `id` may be a v1 id or a v2 id.
*
* @param {object} state Full redux store state tree
* @param {string} id Appeal ID of the appeal to find
* @returns {object} One appeal object or undefined if not found in the array
*/
export function isolateAppeal(state, id) {
return find(
state.disability.status.claimsV2.appeals,
a => a.id === id || (get(a, 'attributes.appealIds') || []).includes(id),
);
}
export function formatDate(date) {
return moment(date, 'YYYY-MM-DD').format('MMMM DD, YYYY');
}
function getHearingType(type) {
const typeMaps = {
video: 'videoconference',
travel_board: 'travel board', // eslint-disable-line camelcase
central_office: 'Washington, DC central office', // eslint-disable-line camelcase
};
return typeMaps[type] || type;
}
/**
* Grabs the matching title and dynamically-generated description for a given current status type
* @typedef {Object} Contents
* @property {string} title a current status type's title
* @property {HTMLElement} description details about the current status, can be any element
* ----------------------------------------------------------------------------------------------
* @typedef {Object} Name
* @property {string} [first] first name
* @property {string} [middle] middle name
* @property {string} [last] last
* @param {Object} appeal
* @param {Name} [name] used for death status type, includes first/middle/last properties
* @returns {Contents}
*/
export function getStatusContents(appeal, name = {}) {
const { status, aoj, programArea } = appeal.attributes;
const appealType = appeal.type;
const statusType = status.type || status;
const details = status.details || {};
const amaDocket = get(appeal, 'attributes.docket.type');
const aojDescription = getAojDescription(aoj);
const contents = {};
switch (statusType) {
case STATUS_TYPES.pendingSoc:
contents.title = 'A Decision Review Officer is reviewing your appeal';
contents.description = (
<p>
The {aojDescription} received your Notice of Disagreement. A Decision
Review Officer (DRO) will review all of the evidence related to your
appeal, including any new evidence you sent. The DRO may contact you
to ask for more evidence or medical exams as needed. When the DRO has
completed their review, they’ll determine whether or not they can
grant your appeal.
</p>
);
break;
case STATUS_TYPES.pendingForm9: {
const formattedSocDate = moment(details.lastSocDate, 'YYYY-MM-DD').format(
'MMMM D, YYYY',
);
contents.title = 'Please review your Statement of the Case';
contents.description = (
<div>
<p>
The {aojDescription} sent you a Statement of the Case on{' '}
{formattedSocDate}. The Statement of the Case explains the reasons
why they couldn’t fully grant your appeal.
</p>
<p>
You’ll have to take one of these actions within 60 days from the
date on the Statement of the Case:
</p>
<ul>
<li>
Submit VA Form 9 to continue your appeal to the Board of Veterans’
Appeals, <strong>or</strong>
</li>
<li>
<a href={DECISION_REVIEW_URL}>
Opt in to the new decision review process
</a>
</li>
</ul>
</div>
);
break;
}
case STATUS_TYPES.pendingCertification:
contents.title =
'The Decision Review Officer is finishing their review of your appeal';
contents.description = (
<p>
The {aojDescription} received your VA Form 9 and will send your appeal
to the Board of Veterans’ Appeals. But first, the Decision Review
Officer must certify that they have finished reviewing all of the
evidence related to your appeal.
</p>
);
break;
case STATUS_TYPES.pendingCertificationSsoc: {
const formattedSocDate = moment(details.lastSocDate, 'YYYY-MM-DD').format(
'MMMM D, YYYY',
);
contents.title = 'Please review your Supplemental Statement of the Case';
contents.description = (
<div>
<p>
The {aojDescription} sent you a Supplemental Statement of the Case
on {formattedSocDate}. This is because one or both of these is true:
</p>
<ul>
<li>
You, your legal representative, your health care provider, or VA
added new evidence to your appeal and asked VA to review it before
certifying to the Board
</li>
<li>
VA determined it needed to provide you with more help to develop
your appeal, such as helping you get treatment records or giving
you a physical exam if needed.
</li>
</ul>
</div>
);
break;
}
case STATUS_TYPES.remandSsoc: {
const formattedSocDate = moment(details.lastSocDate, 'YYYY-MM-DD').format(
'MMMM D, YYYY',
);
contents.title = 'Please review your Supplemental Statement of the Case';
contents.description = (
<p>
The {aojDescription} sent you a Supplemental Statement of the Case on{' '}
{formattedSocDate} because, after completing the remand instructions
from the Board, they couldn’t fully grant your appeal.
</p>
);
break;
}
case STATUS_TYPES.pendingHearingScheduling:
contents.title = 'You’re waiting for your hearing to be scheduled';
contents.description = (
<div>
<p>
You requested a {getHearingType(details.type)} hearing. We'll
schedule your hearing, and, you’ll receive a notice in the mail at
least 30 days before the hearing date.
</p>
{appealType === APPEAL_TYPES.appeal && (
<p>
<strong>Note:</strong> If you have new evidence, you can only
submit it at your hearing or within the 90 days after your
hearing. Please don’t submit additional evidence now.
</p>
)}
</div>
);
break;
case STATUS_TYPES.scheduledHearing: {
const formattedDate = moment(details.date, 'YYYY-MM-DD').format(
'MMMM D, YYYY',
);
contents.title = 'Your hearing has been scheduled';
contents.description = (
<div>
<p>
Your {getHearingType(details.type)} hearing is scheduled for{' '}
{formattedDate} at {details.location}.
</p>
{appealType === APPEAL_TYPES.appeal && (
<p>
<strong>Note:</strong> If you have new evidence, you can only
submit it at your hearing or within the 90 days after your
hearing. Please don’t submit additional evidence now.
</p>
)}
</div>
);
break;
}
case STATUS_TYPES.onDocket:
contents.title = 'Your appeal is waiting to be sent to a judge';
contents.description = (
<div>
<p>
Your appeal is at the Board of Veterans’ Appeals, waiting to be sent
to a Veterans Law Judge. Staff at the Board will make sure your case
is complete, accurate, and ready to be decided by a judge.
</p>
{appealType === APPEAL_TYPES.appeal && (
<p>
<strong>Note:</strong> Please don’t submit additional evidence.
The judge will only consider evidence that VA already has.
</p>
)}
</div>
);
break;
case STATUS_TYPES.atVso:
contents.title = 'Your appeal is with your Veterans Service Organization';
contents.description = (
<p>
{details.vsoName} is reviewing your appeal to make additional
arguments in support of your case. For more information, please
contact {details.vsoName}.
</p>
);
break;
case STATUS_TYPES.decisionInProgress:
contents.title = 'A judge is reviewing your appeal';
contents.description = (
<p>
Your appeal is at the Board of Veterans’ Appeals being reviewed by a
Veterans Law Judge.{' '}
{appealType === APPEAL_TYPES.legacy &&
'If you submit evidence that isn’t already included in your case, it may delay your appeal.'}
</p>
);
break;
case STATUS_TYPES.bvaDevelopment:
contents.title =
'The judge is seeking more information before making a decision';
contents.description = (
<p>
The Board of Veterans’ Appeals is seeking evidence or an outside
opinion from a legal, medical, or other professional in order to make
a decision about your appeal.
</p>
);
break;
case STATUS_TYPES.stayed:
contents.title =
'The Board is waiting until a higher court makes a decision';
contents.description = (
<p>
A higher court has asked the Board of Veterans’ Appeals to hold open a
group of appeals awaiting review. Yours is one of the appeals held
open. The higher court believes that a decision it will make on a
different appeal could affect yours.
</p>
);
break;
case STATUS_TYPES.remand:
case STATUS_TYPES.amaRemand:
case STATUS_TYPES.bvaDecision:
contents.title = 'The Board made a decision on your appeal';
contents.description = (
<div>
<p>
The Board of Veterans’ Appeals sent you a decision on your appeal.
Here’s an overview:
</p>
<Decision
issues={details.issues}
aoj={aoj}
ama={appealType === APPEAL_TYPES.appeal}
boardDecision
/>
<Toggler toggleName={Toggler.TOGGLE_NAMES.cstIncludeDdlBoaLetters}>
<Toggler.Enabled>
<p>
You can download your decision letter online now. You can also
get other letters related to your claims and appeals.
<Link
className="ddl-link vads-c-action-link--blue"
to="/your-claim-letters"
>
Get your decision letters
</Link>
</p>
</Toggler.Enabled>
</Toggler>
</div>
);
break;
case STATUS_TYPES.fieldGrant:
contents.title = `The ${aojDescription} granted your appeal`;
contents.description = (
<p>
The {aojDescription} agreed with you and decided to overturn the
original decision. If this decision changes your disability rating or
eligibility for VA benefits, you should see this change made in 1 to 2
months.
</p>
);
break;
case STATUS_TYPES.withdrawn:
contents.title = 'You withdrew your appeal';
contents.description = (
<p>
You chose not to continue your appeal. If this information is
incorrect, please contact your Veterans Service Organization or
representative for more information.
</p>
);
break;
case STATUS_TYPES.ftr:
contents.title = 'Your appeal was closed';
contents.description = (
<p>
You didn’t take an action VA requested in order to continue your
appeal. If this information is incorrect, or if you want to reopen
your appeal, please contact your Veterans Service Organization or
representative for more information.
</p>
);
break;
case STATUS_TYPES.ramp:
contents.title =
'You opted in to the Rapid Appeals Modernization Program (RAMP)';
contents.description = (
<p>
You chose to participate in the new Supplemental Claim or Higher-Level
Review options. This doesn’t mean that your appeal has been closed. If
this information is incorrect, please contact your Veterans Service
Organization or representative as soon as possible.
</p>
);
break;
case STATUS_TYPES.reconsideration:
contents.title = 'Your Motion for Reconsideration was denied';
contents.description = (
<p>
The Board of Veterans’ Appeals declined to reopen your appeal. Please
contact your Veterans Service Organization or representative for more
information.
</p>
);
break;
case STATUS_TYPES.death: {
const { first, middle, last } = name;
const nameString = `${first || ''} ${middle || ''} ${last || ''}`;
contents.title = 'The appeal was closed';
contents.description = (
<p>
VA records indicate that {startCase(nameString.toLowerCase())} is
deceased, so this appeal has been closed. If this information is
incorrect, please contact your Veterans Service Organization or
representative as soon as possible.
</p>
);
break;
}
case STATUS_TYPES.otherClose:
contents.title = 'Your appeal was closed';
contents.description = (
<p>
Your appeal was dismissed or closed. Please contact your Veterans
Service Organization or representative for more information.
</p>
);
break;
case STATUS_TYPES.merged:
contents.title = 'Your appeal was merged';
contents.description = (
<div>
<p>
Your appeal was merged with another appeal. The Board of Veterans’
Appeals merges appeals so that you can receive a single decision on
as many appealed issues as possible. This appeal was merged with an
older appeal that was closest to receiving a Board decision.
</p>
<p>
Check <Link to="/your-claims">Your claims and appeals</Link> for the
appeal that contains the issues merged from this appeal.
</p>
</div>
);
break;
case STATUS_TYPES.statutoryOptIn:
contents.title =
'You requested a decision review under the Appeals Modernization Act';
contents.description = (
<div>
<p>
A new law, the Veterans Appeals Improvement and Modernization Act,
took effect on February 19, 2019. Although your appeal started
before the new law took effect, you asked for it to be converted
into one of the new decision review options.
</p>
<p>
Check <Link to="/your-claims">Your claims and appeals</Link> for the
decision review that contains the issues from this appeal, or learn
more about{' '}
<a href={DECISION_REVIEW_URL}>
decision reviews under the Appeals Modernization Act
</a>
.
</p>
</div>
);
break;
case STATUS_TYPES.evidentiaryPeriod:
contents.title = 'Your appeals file is open for new evidence';
contents.description = (
<div>
<p>
Because you requested the {getDocketName(amaDocket)} appeal option,
the Board of Veterans’ Appeals will hold your case open for new
evidence for 90 days. You can send new evidence to the Board at:
</p>
<p className="va-address-block">
Board of Veterans’ Appeals
<br />
PO Box 27063
<br />
Washington, DC 20038
<br />
Fax 844-678-8979
</p>
</div>
);
break;
case STATUS_TYPES.postBvaDtaDecision: {
const formattedBvaDecisionDate = moment(
details.bvaDecisionDate,
'YYYY-MM-DD',
).format('MMMM D, YYYY');
const formattedAojDecisionDate = moment(
details.aojDecisionDate,
'YYYY-MM-DD',
).format('MMMM D, YYYY');
contents.title = `The ${aojDescription} corrected an error`;
contents.description = (
<div>
<p>
In the {formattedBvaDecisionDate} decision, a judge at the Board of
Veterans’ Appeals identified an error that needed to be corrected. A
reviewer at the {aojDescription} completed the judge’s instructions
and sent you a new decision on {formattedAojDecisionDate}. Here's an
overview:
</p>
<Decision issues={details.issues} aoj={aoj} />
<Toggler toggleName={Toggler.TOGGLE_NAMES.cstIncludeDdlBoaLetters}>
<Toggler.Enabled>
<p>
You can download your decision letter online now. You can also
get other letters related to your claims and appeals.
<Link
className="ddl-link vads-c-action-link--blue"
to="/your-claim-letters"
>
Get your decision letters
</Link>
</p>
</Toggler.Enabled>
</Toggler>
<p>
If you disagree with either the Board decision or the{' '}
{aojDescription} decision, you can request another review. The
review options available to you depend on which decision you
disagree with.
</p>
</div>
);
break;
}
case STATUS_TYPES.bvaDecisionEffectuation: {
const formattedBvaDecisionDate = moment(
details.bvaDecisionDate,
'YYYY-MM-DD',
).format('MMMM D, YYYY');
const formattedAojDecisionDate = moment(
details.aojDecisionDate,
'YYYY-MM-DD',
).format('MMMM D, YYYY');
contents.title = `The ${aojDescription} corrected an error`;
contents.description = (
<div>
<p>
On {formattedBvaDecisionDate}, a judge at the Board of Veterans’
Appeals made a decision that changes your disability rating or
eligibility for benefits. On {formattedAojDecisionDate}, the{' '}
{aojDescription} sent you a new decision that updates your benefits.
</p>
<p>
If you disagree with either the Board decision or the{' '}
{aojDescription} decision, you can request another review. The
review options available to you depend on which decision you
disagree with.
</p>
</div>
);
break;
}
// TODO: Remove this if Caseflow fixes the typo issue on their end
case 'sc_recieved':
case STATUS_TYPES.scReceived:
contents.title = 'A reviewer is examining your new evidence';
contents.description = (
<div>
<p>
A Supplemental Claim allows you to add new and relevant evidence to
your case. When you filed a Supplemental Claim, you included new
evidence or identified evidence that the {aojDescription} should
obtain.
</p>
{programArea !== 'compensation' && (
<p>
If you have more evidence to submit, you should do so as soon as
possible.
</p>
)}
{programArea === 'compensation' && (
<div>
<p>
If you have more evidence to submit, you should do so as soon as
possible. You can send new evidence to the {aojDescription} at:
</p>
<p className="va-address-block">
Department of Veterans Affairs
<br />
Evidence Intake Center
<br />
PO Box 4444
<br />
Janesville, WI 53547-4444
</p>
</div>
)}
<p>
A reviewer will look at this new evidence, as well as evidence VA
already had, and determine whether it changes the decision. If
needed, they may contact you to ask for more evidence or to schedule
a new medical exam.
</p>
</div>
);
break;
case STATUS_TYPES.hlrReceived:
contents.title =
'A higher-level reviewer is taking a new look at your case';
contents.description = (
<div>
<p>
By requesting a Higher-Level Review, you asked for a higher-level
reviewer at the {aojDescription} to look at your case and determine
whether they can change the decision based on a difference of
opinion or because VA made an error.
</p>
{details.informalConference && (
<p>
You requested an informal conference. The reviewer will contact
you at the phone number you provided to schedule a time to speak
to you and/or your representative. When you speak to the reviewer,
you can say why you think the decision should be changed and
identify errors.
</p>
)}
<p>
<strong>Note:</strong> Please don’t submit additional evidence. The
reviewer will only consider evidence that VA already has.
</p>
</div>
);
break;
case STATUS_TYPES.scDecision:
contents.title = `The ${aojDescription} made a decision`;
contents.description = (
<div>
<p>
The {aojDescription} sent you a decision on your Supplemental Claim.
Here’s an overview:
</p>
<Decision issues={details.issues} aoj={aoj} />
</div>
);
break;
case STATUS_TYPES.hlrDecision:
contents.title = `The ${aojDescription} made a decision`;
contents.description = (
<div>
<p>
The {aojDescription} sent you a decision on your Higher-Level
Review. Here’s an overview:
</p>
<Decision issues={details.issues} aoj={aoj} />
</div>
);
break;
case STATUS_TYPES.hlrDtaError:
contents.title = `The ${aojDescription} is correcting an error`;
contents.description = (
<p>
During their review, the higher-level reviewer identified an error
that must be corrected before deciding your case. If needed, VA may
contact you to ask for more evidence or to schedule a new medical
exam.
</p>
);
break;
case STATUS_TYPES.scClosed:
contents.title = 'Your Supplemental Claim was closed';
contents.description = (
<p>
Your Supplemental Claim was closed. Please contact VA or your Veterans
Service Organization or representative for more information.
</p>
);
break;
case STATUS_TYPES.hlrClosed:
contents.title = 'Your Higher-Level Review was closed';
contents.description = (
<p>
Your Higher-Level Review was closed. Please contact VA or your
Veterans Service Organization or representative for more information.
</p>
);
break;
case STATUS_TYPES.remandReturn:
contents.title =
'Your appeal was returned to the Board of Veterans’ Appeals';
contents.description = (
<p>
The Veterans Benefits Administration finished their work on the remand
and will return your case to the Board of Veterans’ Appeals.
</p>
);
break;
// We need to add content for these, but adding cases for them so that they
// don't get logged to Sentry anymore. This will help prevent other unknown
// statuses from getting overshadowed by these
// Github Tickets
// motion:
// https://github.com/department-of-veterans-affairs/va.gov-team/issues/80665
// pre_docketed:
// https://github.com/department-of-veterans-affairs/va.gov-team/issues/80647
case 'motion':
case 'pre_docketed':
contents.title = 'We don’t know your status';
contents.description = (
<p>We’re sorry, VA.gov will soon be updated to show your status.</p>
);
break;
default:
contents.title = 'We don’t know your status';
contents.description = (
<p>We’re sorry, VA.gov will soon be updated to show your status.</p>
);
Sentry.withScope(scope => {
scope.setExtra('statusType', statusType);
Sentry.captureMessage('appeals-unknown-status-type');
});
}
return contents;
}
export const EVENT_TYPES = {
claimDecision: 'claim_decision',
nod: 'nod',
soc: 'soc',
form9: 'form9',
ssoc: 'ssoc',
certified: 'certified',
hearingHeld: 'hearing_held',
hearingNoShow: 'hearing_no_show',
transcript: 'transcript',
bvaDecision: 'bva_decision',
cavcDecision: 'cavc_decision',
remandReturn: 'remand_return',
rampNotice: 'ramp_notice',
fieldGrant: 'field_grant',
withdrawn: 'withdrawn',
failureToRespond: 'ftr',
rampOptIn: 'ramp',
death: 'death',
merged: 'merged',
reconsideration: 'reconsideration',
vacated: 'vacated',
otherClose: 'other_close',
amaNod: 'ama_nod',
docketChange: 'docket_change',
distributedToVlj: 'distributed_to_vlj',
bvaDecisionEffectuation: 'bva_decision_effectuation',
dtaDecision: 'dta_decision',
scRequest: 'sc_request',
scDecision: 'sc_decision',
scOtherClose: 'sc_other_close',
hlrRequest: 'hlr_request',
hlrDecision: 'hlr_decision',
hlrDtaError: 'hlr_dta_error',
hlrOtherClose: 'hlr_other_close',
statutoryOptIn: 'statutory_opt_in',
};
/**
* Returns an object with the content used in the timeline.
* @param {Object} event
* @returns {Object}
*/
export function getEventContent(event) {
switch (event.type) {
case EVENT_TYPES.claimDecision:
return {
title: 'VA sent you a claim decision',
description: '',
};
case EVENT_TYPES.nod:
return {
title: 'VA received your Notice of Disagreement',
description: '',
};
case EVENT_TYPES.soc:
return {
title: 'VA sent you a Statement of the Case',
description: '',
};
case EVENT_TYPES.form9:
return {
title: 'VA received your Form 9',
description: '',
};
case EVENT_TYPES.ssoc:
return {
title: 'VA sent you a Supplemental Statement of the Case',
description: '',
};
case EVENT_TYPES.certified:
return {
title: 'Your appeal was sent to the Board of Veterans’ Appeals',
description: '',
};
case EVENT_TYPES.hearingHeld:
return {
title: 'You attended a hearing with a Veterans Law Judge',
description: '',
};
case EVENT_TYPES.hearingNoShow:
return {
title: 'You missed your hearing with a Veterans Law Judge',
description: '',
};
case EVENT_TYPES.transcript:
return {
title: 'VA sent you a transcript of your hearing',
description: '',
};
case EVENT_TYPES.bvaDecision:
return {
title: 'Board of Veterans’ Appeals made a decision',
description: '',
};
case EVENT_TYPES.cavcDecision:
return {
title: 'U.S. Court of Appeals for Veterans Claims made a decision',
description: '',
};
case EVENT_TYPES.remandReturn:
return {
title: 'Your appeal was returned to the Board of Veterans’ Appeals',
description: '',
};
case EVENT_TYPES.rampNotice:
return {
title:
'VA sent you a letter about the Rapid Appeals Modernization Program',
description: '',
};
case EVENT_TYPES.fieldGrant:
return {
title: 'VA granted one or more issues',
description: '',
};
case EVENT_TYPES.withdrawn:
return {
title: 'You withdrew your appeal',
description: '',
};
case EVENT_TYPES.failureToRespond:
case EVENT_TYPES.otherClose:
return {
title: 'Your appeal was closed',
description: '',
};
case EVENT_TYPES.rampOptIn:
return {
title: 'You opted in to the Rapid Appeals Modernization Program',
description: '',
};
case EVENT_TYPES.death:
return {
title: 'The appeal was closed',
description: '',
};
case EVENT_TYPES.merged:
return {
title: 'Your appeals were merged',
description: '',
};
case EVENT_TYPES.reconsideration:
return {
title: 'Your Motion for Reconsideration was denied',
description: '',
};
case EVENT_TYPES.vacated:
return {
title: 'Board of Veterans’ Appeals vacated a previous decision',
description: '',
};
case EVENT_TYPES.amaNod:
return {
title: 'Board of Veterans’ Appeals received your appeal',
description: '',
};
case EVENT_TYPES.docketChange:
return {
title: 'You switched appeal options',
description: '',
};
case EVENT_TYPES.distributedToVlj:
return {
title: 'Your appeal was distributed to a Veterans Law Judge',
description: '',
};
case EVENT_TYPES.bvaDecisionEffectuation:
return {
title: 'VA updated your benefits to reflect the Board’s decision',
description: '',
};
case EVENT_TYPES.dtaDecision:
return {
title: 'VA corrected an error and made a new decision',
description: '',
};
case EVENT_TYPES.scRequest:
return {
title: 'VA received your Supplemental Claim request',
description: '',
};
case EVENT_TYPES.scDecision:
case EVENT_TYPES.hlrDecision:
return {
title: 'VA made a new decision',
description: '',
};
case EVENT_TYPES.scOtherClose:
return {
title: 'Your Supplemental Claim was closed',
description: '',
};
case EVENT_TYPES.hlrRequest:
return {
title: 'VA received your Higher-Level Review request',
description: '',
};
case EVENT_TYPES.hlrDtaError:
return {
title: 'VA identified an error that must be corrected',
description: '',
};
case EVENT_TYPES.hlrOtherClose:
return {
title: 'Your Higher-Level Review was closed',
description: '',
};
case EVENT_TYPES.statutoryOptIn:
return {
title:
'You requested a decision review under the Appeals Modernization Act',
description: '',
};
default:
Sentry.withScope(scope => {
scope.setExtra('eventType', event.type);
Sentry.captureMessage('appeals-unknown-event');
});
return null;
}
}
/**
* Creates content reused throughout getNextEvents
* @param {string} [isAma] Whether it is an AMA appeal
* @param {string} [aoj] The agency of original jurisdiction
* @param {string} [prop] Additional text to include at beginning of first paragraph
* @returns {object} Decision review content
*/
export const makeDecisionReviewContent = ({
isAma = false,
aoj = AOJS.vba,
prop = '',
} = {}) => (
<div>
<p>
{prop}
{prop ? ' The judge' : 'A Veterans Law Judge'} will review all of the
available evidence and write a decision. For each issue you’re appealing,
they can decide to:
</p>
<ul className="decision-review-list">
<li>
<strong>Grant:</strong> The judge disagrees with the original decision
and decides in your favor.
</li>
<li>
<strong>Deny:</strong> The judge agrees with the original decision.
</li>
<li>
<strong>Remand:</strong> The judge sends the issue back to the{' '}
{getAojDescription(aoj)} to{' '}
{isAma
? 'correct an error'
: 'gather more evidence or to fix a mistake before deciding whether to grant or deny'}
.
</li>
</ul>
{!isAma && (
<p>
<strong>Note:</strong> About 60% of all cases have at least 1 issue
remanded.
</p>
)}
</div>
);
/**
* Gets 'what's next' content for a given current status type
* @typedef {Object} nextEvent
* @property {string} title header for each NextEvent
* @property {HTMLElement} description formatted content for each NextEvent
* ----------------------------------------------------------------------------------------------
* @typedef {Object} allNextEvents
* @property {string} header a short description to introduce all of the nextEvents
* @property {nextEvent[]} events each contain text content for a NextEvent component
* ----------------------------------------------------------------------------------------------
* @param {Object} appeal
* @returns {allNextEvents} a section description and array containing all next event possibilities
* for a given current status
*/
export function getNextEvents(appeal) {
const { type: currentStatus, details } = appeal.attributes.status;
const appealType = appeal.type;
switch (currentStatus) {
case STATUS_TYPES.pendingSoc: {
return {
header: `What happens next depends on whether the Decision Review Officer has enough
evidence to decide in your favor.`,
events: [
{
title:
'The Veterans Benefits Administration will grant some or all of your appeal',
description: (
<p>
<strong>
If the Decision Review Officer determines that there’s enough
evidence to grant one or more of the issues on your appeal,
</strong>{' '}
they’ll make a new decision. If this decision changes your
disability rating or eligibility for VA benefits, you should
expect this change to be made in 1 to 2 months.
</p>
),
},
{
title:
'The Veterans Benefits Administration will send you a Statement of the Case',
description: (
<p>
<strong>
If the Decision Review Officer determines that there isn’t
enough evidence to fully grant your appeal,
</strong>{' '}
they’ll send you their findings in a document called a Statement
of the Case. You can then decide whether to continue your appeal
to the Board of Veterans’ Appeals, or{' '}
<a href={DECISION_REVIEW_URL}>
opt in to one of the new decision review options
</a>
.
</p>
),
},
],
};
}
case STATUS_TYPES.pendingForm9: {
const formattedSocDate = moment(details.lastSocDate, 'YYYY-MM-DD').format(
'MMMM D, YYYY',
);
return {
header: `If you return VA Form 9 within 60 days, what happens next
depends on whether you also submit new evidence and ask VA to review
it before sending to the Board.`,
events: [
{
title: 'Your appeal will be sent it to the Board',
description: (
<p>
<strong>If you don’t submit new evidence</strong> after the
Statement of the Case on {formattedSocDate}, the Decision Review
Officer will finish their review and send your case to the Board
of Veterans’ Appeals.
</p>
),
},
{
title:
'The Veterans Benefits Administration will send you a Supplemental Statement of the Case',
description: (
<p>
<strong>If you submit new evidence</strong> after the Statement
of the Case on {formattedSocDate} <strong>and</strong> ask VA to
review the evidence first, the Decision Review Officer (DRO)
will need to write a Supplemental Statement of the Case before
sending your case to the Board of Veterans’ Appeals. Otherwise,
the DRO will send your appeal to the Board. Once your appeal has
been sent, new evidence can be submitted directly to the Board
and won’t be reviewed by the Veterans Benefits Administration.
</p>
),
},
],
};
}
case STATUS_TYPES.pendingCertification: {
const formattedSocDate = moment(details.lastSocDate, 'YYYY-MM-DD').format(
'MMMM D, YYYY',
);
return {
header: 'What happens next depends on whether you submit new evidence.',
events: [
{
title: 'Your appeal will be sent to the Board',
description: (
<p>
<strong>If you don’t submit new evidence</strong> after the
Statement of the Case on {formattedSocDate}, the Decision Review
Officer will finish their review and send your case to the Board
of Veterans’ Appeals.
</p>
),
},
{
title:
'The Veterans Benefits Administration will send you a Supplemental Statement of the Case',
description: (
<p>
<strong>If you submit new evidence</strong> after the Statement
of the Case on {formattedSocDate} <strong>and</strong> ask VA to
review the evidence first, the Decision Review Officer (DRO)
will need to write a Supplemental Statement of the Case before
sending your case to the Board of Veterans’ Appeals. Otherwise,
the DRO will send your appeal to the Board. Once your appeal has
been sent, new evidence can be submitted directly to the Board
and won’t be reviewed by the Veterans Benefits Administration.
</p>
),
},
],
};
}
case STATUS_TYPES.pendingCertificationSsoc: {
const formattedSocDate = moment(details.lastSocDate, 'YYYY-MM-DD').format(
'MMMM D, YYYY',
);
return {
header: 'What happens next depends on whether you submit new evidence.',
events: [
{
title: 'Your appeal will be sent to the Board',
description: (
<p>
<strong>If you don’t submit new evidence</strong> after the
Supplemental Statement of the Case on {formattedSocDate}, the
Decision Review Officer will finish their review and send your
case to the Board of Veterans’ Appeals.
</p>
),
},
{
title:
'The Veterans Benefits Administration will send you a Supplemental Statement of the Case',
description: (
<p>
<strong>If you submit new evidence</strong> after the
Supplemental Statement of the Case on {formattedSocDate}{' '}
<strong>and</strong> ask VA to review the evidence first, the
Decision Review Officer (DRO) will need to write a new
Supplemental Statement of the Case before sending your case to
the Board of Veterans’ Appeals. Otherwise, the DRO will send
your appeal to the Board. Once your appeal has been sent, new
evidence can be submitted directly to the Board and won’t be
reviewed by the Veterans Benefits Administration.
</p>
),
},
],
};
}
case STATUS_TYPES.remandSsoc: {
const formattedSocDate = moment(details.lastSocDate, 'YYYY-MM-DD').format(
'MMMM D, YYYY',
);
return {
header: 'What happens next depends on whether you submit new evidence.',
events: [
{
title: 'Your appeal will be returned to the Board',
description: (
<p>
<strong>If you don’t submit new evidence</strong> after the
Supplemental Statement of the Case on {formattedSocDate}, the
Veterans Benefits Administration will finish their work on the
remand and return your case to the Board of Veterans’ Appeals.
</p>
),
},
{
title:
'The Veterans Benefits Administration will send you a Supplemental Statement of the Case',
description: (
<p>
<strong>If you submit new evidence</strong> after the
Supplemental Statement of the Case on {formattedSocDate}{' '}
<strong>and</strong> ask VA to review the evidence first, the
Veterans Benefits Administration will need to write a new
Supplemental Statement of the Case before returning your case to
the Board of Veterans’ Appeals. Otherwise, the Decision Review
Officer will send your appeal to the Board.
</p>
),
},
],
};
}
case STATUS_TYPES.pendingHearingScheduling: {
const eligibleToSwitch = get(
appeal,
'attributes.docket.eligibleToSwitch',
);
const eligibleDescription =
'However, note that this won’t speed up your appeal unless you also switch to the Direct Review appeal option, which can only be done at certain times. See below for more information.';
const ineligibleDescription =
'However, note that this won’t speed up your appeal because your appeal will remain on the Hearing Request docket line and the deadline has passed for switching to a different docket.';
return {
header: '', // intentionally empty
events: [
{
title: `You’ll have your ${getHearingType(details.type)} hearing`,
description: (
<div>
<p>
At your hearing, you and a Veterans Law Judge will have a
conversation, and they’ll ask you questions about your appeal.
Your hearing will be transcribed and added to your appeal
file. The judge won’t make a decision about your appeal at the
hearing.{' '}
<a
href={
appealType === APPEAL_TYPES.appeal
? '/decision-reviews/board-appeal/veterans-law-judge-hearing/'
: '/disability/file-an-appeal/board-of-veterans-appeals/'
}
>
Learn more about hearings.
</a>
</p>
{appeal.type === APPEAL_TYPES.appeal && (
<p>
If you’ve changed your mind about having a hearing, you can
write to the Board of Veterans’ Appeals to withdraw your
hearing request.{' '}
{eligibleToSwitch
? eligibleDescription
: ineligibleDescription}
</p>
)}
</div>
),
},
],
};
}
case STATUS_TYPES.scheduledHearing: {
return {
header: '', // intentionally empty
events: [
{
title: `You’ll have your ${getHearingType(details.type)} hearing`,
description: (
<p>
At your hearing, you and a Veterans Law Judge will have a
conversation, and they’ll ask you questions about your appeal.
Your hearing will be transcribed and added to your appeal file.
The judge won’t make a decision about your appeal at the
hearing.{' '}
<a
href={
appealType === APPEAL_TYPES.appeal
? '/decision-reviews/board-appeal/veterans-law-judge-hearing/'
: '/disability/file-an-appeal/board-of-veterans-appeals/'
}
>
Learn more about hearings
</a>
, including how to prepare for, reschedule, or cancel your
hearing.
</p>
),
},
],
};
}
case STATUS_TYPES.evidentiaryPeriod: {
return {
header: '', // intentionally empty
events: [
{
title: 'The Board will make a decision',
description: makeDecisionReviewContent({
isAma: appeal.type === APPEAL_TYPES.appeal,
aoj: appeal.attributes.aoj,
prop:
'Once the 90 day time period for submitting new evidence is closed, your case will be ready to go to a Veterans Law Judge. Before it’s reviewed by a judge, some Veterans Service Organizations will ask for time to make additional arguments in support of your case.',
}),
},
],
};
}
case STATUS_TYPES.atVso: {
return {
header: '', // intentionally empty
events: [
{
title: 'The Board will make a decision',
description: makeDecisionReviewContent({
isAma: appeal.type === APPEAL_TYPES.appeal,
aoj: appeal.attributes.aoj,
prop:
'Once your representative has completed their review, your case will be ready to go to a Veterans Law Judge.',
}),
},
],
};
}
case STATUS_TYPES.onDocket:
case STATUS_TYPES.decisionInProgress: {
return {
header: '', // intentionally empty
events: [
{
title: 'The Board will make a decision',
description: makeDecisionReviewContent({
isAma: appeal.type === APPEAL_TYPES.appeal,
aoj: appeal.attributes.aoj,
}),
},
],
};
}
case STATUS_TYPES.bvaDevelopment:
case STATUS_TYPES.stayed:
return {
header: '', // intentionally empty
events: [
{
title: 'The Board will make a decision',
description: makeDecisionReviewContent(),
},
],
};
case STATUS_TYPES.remand: {
return {
header: '', // intentionally empty
events: [
{
title:
'The Veterans Benefits Administration completes the remand instructions',
description: (
<p>
They may contact you to request more evidence or medical exams
as needed. When they’ve completed the remand instructions,
they’ll determine whether or not they can grant your appeal. If
not, your appeal will return to the Board of Veterans’ Appeals
for a new decision.
</p>
),
},
],
};
}
case STATUS_TYPES.amaRemand:
return {
header: '', // intentionally empty
events: [
{
title: 'A reviewer will correct the error',
description: (
<p>
Because the judge identified an error, a reviewer at the{' '}
{getAojDescription(appeal.attributes.aoj)} will correct the
error based on the judge’s instructions. You’ll receive a new
decision in the mail. If needed, the reviewer may contact you to
ask for more evidence or to schedule a new medical exam.
</p>
),
},
],
};
case STATUS_TYPES.scReceived: {
return {
header: '', // intentionally empty
events: [
{
title: 'The reviewer will make a new decision',
description: (
<p>
The {getAojDescription(appeal.attributes.aoj)} will send you a
new decision in the mail.
</p>
),
},
],
};
}
case STATUS_TYPES.hlrReceived: {
return {
header: '', // intentionally empty
events: [
{
title: 'The higher-level reviewer will make a new decision',
description: (
<p>
The {getAojDescription(appeal.attributes.aoj)} will send you a
new decision in the mail. Your review may take longer if VA
needs to obtain records or schedule a new exam to correct an
error.
</p>
),
},
],
};
}
case STATUS_TYPES.hlrDtaError:
return {
header: '', // intentionally empty
events: [
{
title: 'A reviewer will make a new decision',
description: (
<p>
The {getAojDescription(appeal.attributes.aoj)} will send you a
new decision in the mail. Your review may take longer than the
expected 4–5 months because VA needs to correct an error before
completing its review.
</p>
),
},
],
};
default:
return {
header: '', // intentionally empty
events: [],
};
}
}
const DECISION_REVIEW_OPTIONS = {
supplementalClaim: 'supplemental_claim',
higherLevelReview: 'higher_level_review',
appeal: 'appeal',
cavc: 'cavc',
};
/**
* Takes an alert type and returns its display content and related CSS classes
* @typedef {object} alertInput
* @property {string} type one of ALERT_TYPES as returned by vets-api
* @property {object} details necessary dynamic info for each alert type, properties vary per type
* @param {alert} type each alert can have one of several types as defined by ALERT_TYPES
* ------------------------------------------------------------------------------------------------
* @typedef {object} alertOutput
* @property {string} title Used for the alert header
* @property {HTMLElement} description Some descriptive text for the alert body
* @property {string} displayType Segments the alert into either 'take action' or 'info' buckets
* @property {string} type Pass-through for the input type. Should be one of ALERT_TYPES
* ------------------------------------------------------------------------------------------------
* @param {alertInput} alert has some properties we match against to generate an alert's content
* @returns {alertOutput} dynamically-generated title, description, and styling properties
*/
export function getAlertContent(alert, appealIsActive) {
const { type, details } = alert;
switch (type) {
case ALERT_TYPES.form9Needed: {
const formattedDueDate = formatDate(details.dueDate);
return {
title: `Return VA Form 9 by ${formattedDueDate} in order to continue your appeal`,
description: (
<div>
<p>
A blank VA Form 9 was included with your Statement of the Case.
You can continue your appeal to the Board of Veterans’ Appeals by
submitting this form. When you fill it out, you can also request a
hearing with a Veterans Law Judge if you’d like one.
</p>
<p>
If you need help understanding your Statement of the Case or
completing the VA Form 9, contact your Veterans Service
Organization or representative.
</p>
<p>
You may also opt in to the new decision review process. You have
60 days from the date on the Statement of the Case to{' '}
<a href={DECISION_REVIEW_URL}>
opt in to one of the new decision review options
</a>
.
</p>
</div>
),
displayType: 'take_action',
type,
};
}
case ALERT_TYPES.scheduledHearing: {
const formattedDate = formatDate(details.date);
return {
title: <span>Your hearing is scheduled for {formattedDate}</span>,
description: '', // intentionally empty
displayType: 'take_action',
type,
};
}
case ALERT_TYPES.hearingNoShow: {
const formattedDate = formatDate(details.date);
const formattedDueDate = formatDate(details.dueDate);
return {
title: `You missed your hearing on ${formattedDate}`,
description: (
<div>
<p>
You were scheduled for a hearing on {formattedDate}, but VA
records show that you didn’t attend. If you want to request a new
hearing, you’ll need to send the Board of Veterans’ Appeals a
letter that explains why you didn’t go to the hearing. You’ll need
to send this letter by {formattedDueDate}.
</p>
<p className="va-address-block">
Board of Veterans’ Appeals
<br />
PO Box 27063
<br />
Washington, DC 20038
<br />
Fax 844-678-8979
</p>
<p>
Please contact your Veterans Service Organization or
representative for more information.
</p>
</div>
),
displayType: 'take_action',
type,
};
}
case ALERT_TYPES.heldForEvidence: {
const formattedDueDate = formatDate(details.dueDate);
return {
title: 'Your appeals case is being held open',
description: (
<div>
<p>
You or your representative asked the Board of Veterans’ Appeals to
hold your case open while you gather more evidence to support your
appeal. Please submit your evidence to the Board by{' '}
{formattedDueDate}.
</p>
<p className="va-address-block">
Board of Veterans’ Appeals
<br />
PO Box 27063
<br />
Washington, DC 20038
<br />
Fax 844-678-8979
</p>
</div>
),
displayType: 'take_action',
type,
};
}
case ALERT_TYPES.evidentiaryPeriod: {
const formattedDueDate = formatDate(details.dueDate);
return {
title: `Submit new evidence before ${formattedDueDate}`,
description: (
<p>
If you have new evidence to submit, you must send it to the Board of
Veterans’ Appeals by {formattedDueDate}. Evidence received after
this date can’t be considered by the Veterans Law Judge.
</p>
),
displayType: 'take_action',
type,
};
}
case ALERT_TYPES.rampEligible: {
const formattedDate = formatDate(details.date);
return {
title:
'This appeal is eligible for the Rapid Appeals Modernization Program',
description: (
<div>
<p>
On {formattedDate}, VA sent you a letter to let you know about a
new program called the Rapid Appeals Modernization Program (RAMP).
The Veterans Appeals Improvement and Modernization Act will create
new options in 2019 for Veterans seeking review of VA decisions.
RAMP is a program that allows you to opt in to two of the new
options for review before the new law takes effect. For more
information, review the fact sheet that was enclosed with the
letter.
</p>
<p>
In order to take part in this program, you must return the RAMP
Opt-in Election form. If you choose to participate in RAMP, VA
will withdraw all of your eligible appeals and instead review your
case using the option you select. If you don’t want to participate
in RAMP and would like to continue your appeal under the existing
process, you don’t need to take any action.
</p>
</div>
),
displayType: 'info',
type,
};
}
case ALERT_TYPES.rampIneligible: {
const statusDescription = appealIsActive
? 'is active at the Board of Veterans’ Appeals'
: 'is closed';
const formattedDate = formatDate(details.date);
return {
title:
'This appeal is not eligible for the Rapid Appeals Modernization Program',
description: (
<p>
On {formattedDate}, VA sent you a letter to let you know about a new
program called the Rapid Appeals Modernization Program (RAMP).
However, this appeal isn’t eligible for RAMP because it{' '}
{statusDescription}. If you have other appeals, they may be eligible
for RAMP.
</p>
),
displayType: 'info',
type,
};
}
case ALERT_TYPES.decisionSoon:
return {
title: 'Decision soon',
description: (
<p>
Your appeal will soon receive a Board decision. Submitting new
evidence at this time could delay review of your appeal. If you’ve
moved recently, please make sure that VA has your up-to-date mailing
address.
</p>
),
displayType: 'info',
type,
};
case ALERT_TYPES.blockedByVso:
return {
title: 'A judge currently can’t review your appeal',
description: (
<p>
Your appeal is eligible to be assigned to a judge based on its place
in line, but they’re prevented from reviewing your appeal because
your Veterans Service Organization, {details.vsoName}, is reviewing
it right now. For more information, please contact your Veterans
Service Organization or representative.
</p>
),
displayType: 'info',
type,
};
case ALERT_TYPES.cavcOption: {
const formattedDueDate = formatDate(details.dueDate);
return {
title: 'What if I disagree with my decision?',
description: (
<div>
<p>
If you disagree with the Board’s decision, you can appeal to the
Court of Appeals for Veterans Claims. You’ll need to hire a
VA-accredited attorney to represent you, or you may represent
yourself. You’ll need to file your Court appeal by{' '}
{formattedDueDate}. For more information, you can:
</p>
<ul>
<li>
Review the document “Your Rights to Appeal Our Decision”
enclosed with the Board’s decision
</li>
<li>
Visit the{' '}
<a href="https://www.uscourts.cavc.gov/appeal.php">
Court’s website
</a>
</li>
<li>
Contact your Veterans Service Organization or representative.
</li>
</ul>
</div>
),
// displayType is blank because it doesn't apply; this gets pulled out and displayed as a
// non-alert after "What happens next?"
displayType: '',
type,
};
}
case ALERT_TYPES.amaPostDecision: {
const formattedDecisionDate = formatDate(details.decisionDate);
const formattedDueDate = formatDate(details.dueDate);
const formattedCavcDueDate = formatDate(details.cavcDueDate);
return {
title: `What if I disagree with the ${formattedDecisionDate} decision?`,
description: (
<div>
<p>
If you disagree with VA’s decision, you can choose one of the
following review options to continue your case:
</p>
<ul className="appeals-next-list appeals-next-list-no-separator">
{details.availableOptions.includes(
DECISION_REVIEW_OPTIONS.supplementalClaim,
) && (
<li className="next-event">
<h3>Add new and relevant evidence</h3>
<p>
A reviewer will determine whether the new evidence changes
the decision. This option is called a{' '}
<a href="/decision-reviews/supplemental-claim">
Supplemental Claim
</a>
. <strong>Available until {formattedDueDate}.</strong>
</p>
</li>
)}
{details.availableOptions.includes(
DECISION_REVIEW_OPTIONS.higherLevelReview,
) && (
<li className="next-event">
<h3>Ask for a new look from a higher-level reviewer</h3>
<p>
A higher-level reviewer will look at your case and determine
whether the decision can be changed based on a difference of
opinion or because VA made an error. This option is called a
<a href="/decision-reviews/higher-level-review">
Higher-Level Review
</a>
. <strong>Available until {formattedDueDate}.</strong>
</p>
</li>
)}
{details.availableOptions.includes(
DECISION_REVIEW_OPTIONS.appeal,
) && (
<li className="next-event">
<h3>Appeal to a Veterans Law Judge</h3>
<p>
Appeal to a Veterans Law Judge. A judge at the Board of
Veterans’ Appeals in Washington, D.C. will review your case.
This option is called a{' '}
<a href="/decision-reviews/board-appeal">Board Appeal</a>{' '}
<strong>Available until {formattedDueDate}.</strong>
</p>
</li>
)}
{details.availableOptions.includes(
DECISION_REVIEW_OPTIONS.cavc,
) && (
<li className="next-event">
<h3>
Appeal to the U.S. Court of Appeals for Veterans Claims
</h3>
<p>
The court will review the Board’s decision. You can hire an
attorney to represent you, or you may represent yourself.{' '}
<strong>Available until {formattedCavcDueDate}.</strong>
</p>
</li>
)}
</ul>
<h4>Not available for this decision</h4>
<ul>
{!details.availableOptions.includes(
DECISION_REVIEW_OPTIONS.supplementalClaim,
) && <li>Add new and relevant evidence (Supplemental Claim)</li>}
{!details.availableOptions.includes(
DECISION_REVIEW_OPTIONS.higherLevelReview,
) && (
<li>
Ask for a new look from a higher-level reviewer (Higher-Level
Review)
</li>
)}
{!details.availableOptions.includes(
DECISION_REVIEW_OPTIONS.appeal,
) && <li>Appeal to a Veterans Law Judge (Board Appeal)</li>}
</ul>
{details.availableOptions.includes(
DECISION_REVIEW_OPTIONS.cavc,
) && (
<p>
Your decision has information on additional ways you and/or your
representative can address errors.
</p>
)}
<p>
<a href={DECISION_REVIEW_URL}>
Learn more about your decision review options.
</a>
</p>
</div>
),
// displayType is blank because it doesn't apply; this gets pulled out and displayed as a
// non-alert after "What happens next?"
displayType: '',
type,
};
}
default:
return {
title: '',
description: null,
displayType: '',
type,
};
}
}
export const UNKNOWN_STATUS = 'unknown';
/**
* Tests an http error response for an errors array and status property for the
* first error in the array. Returns the status code or 'unknown'
* @param {Object} response error response object from vets-api
* @returns {string} status code or 'unknown'
*/
export const getErrorStatus = response => {
if (response instanceof Error) {
Sentry.withScope(scope => {
scope.setTag('location', 'getStatus');
Sentry.captureException(response);
});
}
return response?.errors?.[0]?.status ?? UNKNOWN_STATUS;
};
// Series of utility functions to sort claims and appeals by last updated date
/**
*
* @param {Object} appeal
* @returns {string}
*/
const getAppealDate = appeal => {
const { events } = appeal.attributes;
return events && events.length ? events[events.length - 1].date : '0';
};
/**
*
* @param {Object} claim
* @returns {string}
*/
const getClaimDate = claim => {
// START lighthouse_migration
const { claimPhaseDates, phaseChangeDate } = claim.attributes;
return phaseChangeDate || claimPhaseDates?.phaseChangeDate || '0';
// END lighthouse_migration
};
/**
*
* @param {Object} item
* @returns {string}
*/
const getDate = item => {
if (!item.attributes) {
return '0';
}
return appealTypes.includes(item.type)
? getAppealDate(item)
: getClaimDate(item);
};
/**
*
* @param {Object} item1
* @param {Object} item2
* @returns {-1|1|0}
*/
export function sortByLastUpdated(item1, item2) {
const lastUpdatedDate1 = getDate(item1);
const lastUpdatedDate2 = getDate(item2);
if (moment(lastUpdatedDate1).isAfter(lastUpdatedDate2)) {
return -1;
}
if (moment(lastUpdatedDate1).isBefore(lastUpdatedDate2)) {
return 1;
}
return 0;
}
// export function sortByInProgress(item1, item2) {
// }
export function getVisibleRows(list, currentPage) {
const currentIndex = (currentPage - 1) * ITEMS_PER_PAGE;
if (!list.length) {
return list;
}
return list.slice(currentIndex, currentIndex + ITEMS_PER_PAGE);
}
/**
* Calculate the item range based on the current page and number of rows/page.
* This is used to
* @param {Number} page - current page
* @param {Number} totalItems - total number of entries
* @returns
*/
export const getPageRange = (page, totalItems) => {
const firstItem = (page - 1) * ITEMS_PER_PAGE + 1;
const itemsLeftToShow = totalItems - (page - 1) * ITEMS_PER_PAGE;
const lastItem =
itemsLeftToShow > ITEMS_PER_PAGE
? firstItem + ITEMS_PER_PAGE - 1
: firstItem + itemsLeftToShow - 1;
return {
start: firstItem,
end: lastItem,
};
};