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

Add Hearing icon to queue/case views #8904

Merged
merged 36 commits into from
Feb 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8d5539f
pull hearing data in task_serializer
joeyyang Jan 23, 2019
8d1d248
show hearing badge in frontend
joeyyang Jan 23, 2019
dd44fb2
tweak negative margin on HearingBadge
joeyyang Jan 23, 2019
a08efa0
only show the hearing badge if the disposition type is 'held'
joeyyang Jan 23, 2019
3184aed
add hearing badge to all TaskTables
joeyyang Jan 23, 2019
16a1331
Merge branch 'master' into joey-hearing-badge-queue
joeyyang Jan 23, 2019
ccc3861
Merge branch 'master' into joey-hearing-badge-queue
joeyyang Jan 24, 2019
a0be4f4
load hearing data asynchronously
joeyyang Jan 24, 2019
93894f7
Merge branch 'joey-hearing-badge-queue' of https://github.com/departm…
joeyyang Jan 24, 2019
322c8ac
only send back the most recently held hearing
joeyyang Jan 24, 2019
936b20a
Merge branch 'joey-hearing-badge-queue' of https://github.com/departm…
joeyyang Jan 24, 2019
26f9c75
don't init most recent hearing to null
joeyyang Jan 24, 2019
da79fd9
only check for disposition == 'held' on the backend
joeyyang Jan 25, 2019
ae5ab73
Merge branch 'master' into joey-hearing-badge-queue
joeyyang Jan 25, 2019
f9700e3
Merge branch 'master' into joey-hearing-badge-queue
joeyyang Jan 28, 2019
4df1966
merge branch 'master' into 'joey-hearing-badge-queue
joeyyang Jan 29, 2019
25fe8cc
remove HearingBadgeLoader, allow HearingBadge to accept either a task…
joeyyang Feb 1, 2019
310e0a8
joey-hearing-badge-queue' of https://github.com/department-of-veteran…
joeyyang Feb 1, 2019
e4e93a6
check AMA and legacy hearing dispositions for symbol and string
joeyyang Feb 1, 2019
b43f0ea
add hearing disposition types constants file
joeyyang Feb 1, 2019
9f58a95
merge branch 'master' into 'joey-hearing-badge-queue'
joeyyang Feb 1, 2019
c001aca
use constant on backend
joeyyang Feb 1, 2019
5a60288
update column numbers in tests to account for hearing badge column
joeyyang Feb 4, 2019
a90ebec
Merge branch 'master' into joey-hearing-badge-queue
joeyyang Feb 4, 2019
9a1d4f7
update tests to account for extra table column
joeyyang Feb 4, 2019
c54a4ed
Merge branch 'master' into joey-hearing-badge-queue
joeyyang Feb 4, 2019
f0c308a
update one more test
joeyyang Feb 5, 2019
7d7c31b
Merge branch 'joey-hearing-badge-queue' of https://github.com/departm…
joeyyang Feb 5, 2019
7824062
Merge branch 'master' into joey-hearing-badge-queue
Feb 5, 2019
4736966
Merge branch 'master' into joey-hearing-badge-queue
Feb 5, 2019
18e288b
Merge branch 'master' into joey-hearing-badge-queue
Feb 5, 2019
8d06a4f
Merge branch 'master' into joey-hearing-badge-queue
Feb 5, 2019
df800b5
Merge branch 'master' into joey-hearing-badge-queue
Feb 5, 2019
55f9005
merge branch 'master' into 'joey-hearing-badge-queue'
joeyyang Feb 5, 2019
6dd17c0
Merge branch 'joey-hearing-badge-queue' of https://github.com/departm…
joeyyang Feb 6, 2019
e2bd1fd
Merge branch 'master' into joey-hearing-badge-queue
joeyyang Feb 6, 2019
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
20 changes: 20 additions & 0 deletions app/controllers/appeals_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,26 @@ def power_of_attorney
}
end

def hearings
most_recently_held_hearing = appeal.hearings
.select { |hearing| hearing.disposition.to_s == Constants.HEARING_DISPOSITION_TYPES.held }
.max_by(&:scheduled_for)

render json:
if most_recently_held_hearing
{
held_by: most_recently_held_hearing.judge.present? ? most_recently_held_hearing.judge.full_name : "",
viewed_by_judge: !most_recently_held_hearing.hearing_views.empty?,
date: most_recently_held_hearing.scheduled_for,
type: most_recently_held_hearing.readable_request_type,
external_id: most_recently_held_hearing.external_id,
disposition: most_recently_held_hearing.disposition
}
else
{}
end
end

# For legacy appeals, veteran address and birth/death dates are
# the only data that is being pulled from BGS, the rest are from VACOLS for now
def veteran
Expand Down
1 change: 1 addition & 0 deletions client/app/queue/AssignedCasesPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class AssignedCasesPage extends React.Component<Props> {
onTaskAssignment={(params) => props.reassignTasksToUser(params)}
selectedTasks={selectedTasks} />
<TaskTable
includeHearingBadge
includeSelect
includeDetailsLink
includeType
Expand Down
1 change: 1 addition & 0 deletions client/app/queue/AttorneyTaskListView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export default (connect(mapStateToProps, mapDispatchToProps)(AttorneyTaskListVie
const TaskTableTab = ({ description, tasks }) => <React.Fragment>
<p className="cf-margin-top-0" >{description}</p>
<TaskTable
includeHearingBadge
includeDetailsLink
includeType
includeDocketNumber
Expand Down
1 change: 1 addition & 0 deletions client/app/queue/BeaamAppealListView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class BeaamListView extends React.PureComponent {
{messages.error.detail}
</Alert>}
<TaskTable
includeHearingBadge
includeDetailsLink
includeType
includeDocketNumber
Expand Down
16 changes: 11 additions & 5 deletions client/app/queue/CaseListTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { clearCaseListSearch } from './CaseList/CaseListActions';
import { DateString } from '../util/DateUtil';
import { renderAppealType } from './utils';
import COPY from '../../COPY.json';
import HEARING_DISPOSITION_TYPES from '../../constants/HEARING_DISPOSITION_TYPES.json';

const currentAssigneeStyling = css({
color: COLORS.GREEN
Expand Down Expand Up @@ -77,14 +78,19 @@ class CaseListTable extends React.PureComponent {
}
];

const doAnyAppealsHaveHearings = Boolean(_.find(this.props.appeals, (appeal) => {
return appeal.hearings.length;
}));
const doAnyAppealsHaveHeldHearings = Boolean(
_.find(this.props.appeals, (appeal) => {
return appeal.hearings.
filter((hearing) => hearing.disposition === HEARING_DISPOSITION_TYPES.held).
length;
}));

if (doAnyAppealsHaveHearings) {
if (doAnyAppealsHaveHeldHearings) {
const hearingColumn = {
valueFunction: (appeal) => {
const hearings = appeal.hearings.sort((h1, h2) => h1.date < h2.date ? 1 : -1);
const hearings = appeal.hearings.
filter((hearing) => hearing.disposition === HEARING_DISPOSITION_TYPES.held).
sort((h1, h2) => h1.date < h2.date ? 1 : -1);

return <HearingBadge hearing={hearings[0]} />;
}
Expand Down
3 changes: 3 additions & 0 deletions client/app/queue/ColocatedTaskListView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ const NewTasksTab = connect(
return <React.Fragment>
<p className="cf-margin-top-0">{COPY.COLOCATED_QUEUE_PAGE_NEW_TASKS_DESCRIPTION}</p>
<TaskTable
includeHearingBadge
includeDetailsLink
includeTask
includeRegionalOffice={props.belongsToHearingSchedule}
Expand All @@ -140,6 +141,7 @@ const PendingTasksTab = connect(
return <React.Fragment>
<p className="cf-margin-top-0">{COPY.COLOCATED_QUEUE_PAGE_PENDING_TASKS_DESCRIPTION}</p>
<TaskTable
includeHearingBadge
includeDetailsLink
includeTask
includeRegionalOffice={props.belongsToHearingSchedule}
Expand All @@ -161,6 +163,7 @@ const OnHoldTasksTab = connect(
return <React.Fragment>
<p className="cf-margin-top-0">{COPY.COLOCATED_QUEUE_PAGE_ON_HOLD_TASKS_DESCRIPTION}</p>
<TaskTable
includeHearingBadge
includeDetailsLink
includeTask
includeRegionalOffice={props.belongsToHearingSchedule}
Expand Down
1 change: 1 addition & 0 deletions client/app/queue/JudgeDecisionReviewTaskListView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class JudgeDecisionReviewTaskListView extends React.PureComponent {
</p>;
} else {
tableContent = <TaskTable
includeHearingBadge
includeTask
includeDetailsLink
includeDocumentId
Expand Down
2 changes: 2 additions & 0 deletions client/app/queue/OrganizationQueue.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(OrganizationQueue);
const UnassignedTaskTableTab = ({ description, tasks, organizationName }) => <React.Fragment>
<p className="cf-margin-top-0">{description}</p>
<TaskTable
includeHearingBadge
includeDetailsLink
includeTask
includeRegionalOffice={organizationName === 'Hearings Management'}
Expand All @@ -146,6 +147,7 @@ const UnassignedTaskTableTab = ({ description, tasks, organizationName }) => <Re
const TaskTableWithUserColumnTab = ({ description, tasks, organizationName }) => <React.Fragment>
<p className="cf-margin-top-0">{description}</p>
<TaskTable
includeHearingBadge
includeDetailsLink
includeTask
includeRegionalOffice={organizationName === 'Hearings Management'}
Expand Down
6 changes: 6 additions & 0 deletions client/app/queue/QueueActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { associateTasksWithAppeals,
prepareAllTasksForStore,
extractAppealsAndAmaTasks,
prepareMostRecentlyHeldHearingForStore,
prepareTasksForStore } from './utils';
import { ACTIONS } from './constants';
import { hideErrorMessage, showErrorMessage, showSuccessMessage } from './uiReducer/uiActions';
Expand Down Expand Up @@ -157,6 +158,11 @@ export const setAppealDocCount = (appealId: string, docCount: number) => ({
}
});

export const setMostRecentlyHeldHearingForAppeal = (appealId: string, hearing: Object) => ({
type: ACTIONS.SET_MOST_RECENTLY_HELD_HEARING_FOR_APPEAL,
payload: prepareMostRecentlyHeldHearingForStore(appealId, hearing)
});

export const setDecisionOptions = (opts: Object) => (dispatch: Dispatch) => {
dispatch(hideErrorMessage());
dispatch({
Expand Down
1 change: 1 addition & 0 deletions client/app/queue/UnassignedCasesPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class UnassignedCasesPage extends React.PureComponent<Props> {
}
{!this.props.distributionCompleteCasesLoading &&
<TaskTable
includeHearingBadge
includeSelect
includeDetailsLink
includeType
Expand Down
88 changes: 68 additions & 20 deletions client/app/queue/components/HearingBadge.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import PropTypes from 'prop-types';
import { css } from 'glamor';
import _ from 'lodash';
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import Tooltip from '../../components/Tooltip';
import { COLORS } from '../../constants/AppConstants';

import ApiUtil from '../../util/ApiUtil';
import { DateString } from '../../util/DateUtil';
import { setMostRecentlyHeldHearingForAppeal } from '../QueueActions';

/**
* This component can accept either a Hearing object or a Task object.
* e.g.,
* <HearingBadge hearing={hearing} />
* <HearingBadge task={task} />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me know what you think of this API

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it!

*/

const badgeStyling = css({
display: 'inline-block',
Expand All @@ -30,27 +42,63 @@ const listStyling = css({
}
});

const HearingBadge = ({ hearing }) => {
if (!hearing) {
return null;
class HearingBadge extends React.PureComponent {
componentDidMount = () => {
if (!this.props.mostRecentlyHeldHearingForAppeal && !this.props.hearing) {
ApiUtil.get(`/appeals/${this.props.externalId}/hearings`).then((response) => {
this.props.setMostRecentlyHeldHearingForAppeal(this.props.externalId, JSON.parse(response.text));
});
}
}

render = () => {
const hearing = this.props.mostRecentlyHeldHearingForAppeal || this.props.hearing;

if (!hearing || !hearing.date) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is meant to guard against the empty hearing badge bug by guarding against this case:

hearing = {
  heldBy: null, 
  date: null,
  ...: null,
}

return null;
}

const tooltipText = <div>
This case has a hearing associated with it.
<ul {...listStyling}>
<li>Judge: <strong>{hearing.heldBy}</strong></li>
<li>Disposition: <strong>{_.startCase(hearing.disposition)}</strong></li>
<li>Date: <strong><DateString date={hearing.date} /></strong></li>
<li>Type: <strong>{_.startCase(hearing.type)}</strong></li>
</ul>
</div>;

return <div {...css({ marginRight: '-2.5rem' })} className="cf-hearing-badge">
<Tooltip id={`badge-${hearing.id}`} text={tooltipText} position="bottom">
<span {...badgeStyling}>H</span>
</Tooltip>
</div>;
}
}

const tooltipText = <div>
This case has a hearing associated with it.
<ul {...listStyling}>
<li>Judge: <strong>{hearing.heldBy}</strong></li>
<li>Disposition: <strong>{_.startCase(hearing.disposition)}</strong></li>
<li>Date: <strong><DateString date={hearing.date} /></strong></li>
<li>Type: <strong>{_.startCase(hearing.type)}</strong></li>
</ul>
</div>;

// We expect this badge to be shown in a table, so we use this to get rid of the standard table padding.
return <div {...css({ marginRight: '-3rem' })} className="cf-hearing-badge">
<Tooltip id={`badge-${hearing.id}`} text={tooltipText} position="bottom">
<span {...badgeStyling}>H</span>
</Tooltip>
</div>;
HearingBadge.propTypes = {
task: PropTypes.object,
hearing: PropTypes.object
};

export default HearingBadge;
const mapStateToProps = (state, ownProps) => {
let externalId, hearing;

if (ownProps.hearing) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accept either a hearing or a task

hearing = ownProps.hearing;
} else if (ownProps.task) {
externalId = ownProps.task.appeal.externalId;
}

return {
hearing,
externalId,
mostRecentlyHeldHearingForAppeal: state.queue.mostRecentlyHeldHearingForAppeal[externalId] || null
};
};

const mapDispatchToProps = (dispatch) => bindActionCreators({
setMostRecentlyHeldHearingForAppeal
}, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(HearingBadge);
2 changes: 1 addition & 1 deletion client/app/queue/components/TaskTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class TaskTableUnconnected extends React.PureComponent<Props> {
caseHearingColumn = () => {
return this.props.includeHearingBadge ? {
header: '',
valueFunction: (task: TaskWithAppeal) => <HearingBadge hearing={task.appeal.hearings[0]} />
valueFunction: (task: TaskWithAppeal) => <HearingBadge task={task} />
} : null;
}

Expand Down
2 changes: 2 additions & 0 deletions client/app/queue/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export const ACTIONS = {
ERROR_TASKS_AND_APPEALS_OF_ATTORNEY: 'ERROR_TASKS_AND_APPEALS_OF_ATTORNEY',
SET_SELECTION_OF_TASK_OF_USER: 'SET_SELECTION_OF_TASK_OF_USER',
SET_SELECTED_ASSIGNEE_OF_USER: 'SET_SELECTED_ASSIGNEE_OF_USER',
SET_MOST_RECENTLY_HELD_HEARING_FOR_APPEAL: 'SET_MOST_RECENTLY_HELD_HEARING_FOR_APPEAL',
ERROR_ON_RECEIVE_HEARING_FOR_APPEAL: 'ERROR_ON_RECEIVE_HEARING_FOR_APPEAL',
START_ASSIGN_TASKS_TO_USER: 'START_ASSIGN_TASKS_TO_USER',
SET_PENDING_DISTRIBUTION: 'SET_PENDING_DISTRIBUTION',
RECEIVE_ALL_ATTORNEYS: 'RECEIVE_ALL_ATTORNEYS',
Expand Down
9 changes: 9 additions & 0 deletions client/app/queue/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const initialState = {
claimReviews: {},
editingIssue: {},
docCountForAppeal: {},
mostRecentlyHeldHearingForAppeal: {},
newDocsForAppeal: {},
specialIssues: {},

Expand Down Expand Up @@ -171,6 +172,14 @@ export const workQueueReducer = (state: QueueState = initialState, action: Objec
}
}
});
case ACTIONS.SET_MOST_RECENTLY_HELD_HEARING_FOR_APPEAL:
return update(state, {
mostRecentlyHeldHearingForAppeal: {
[action.payload.appealId]: {
$set: action.payload.hearing
}
}
});
case ACTIONS.SET_DECISION_OPTIONS:
return update(state, {
stagedChanges: {
Expand Down
1 change: 1 addition & 0 deletions client/app/queue/types/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export type QueueState = {|
claimReviews: ClaimReviews,
editingIssue: Object,
docCountForAppeal: {[string]: Object},
mostRecentlyHeldHearingForAppeal: {[string]: Object},
stagedChanges: {
appeals: {[string]: Object},
taskDecision: {
Expand Down
14 changes: 14 additions & 0 deletions client/app/queue/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ export const getUndecidedIssues = (issues: Issues) => _.filter(issues, (issue) =
}
});

export const prepareMostRecentlyHeldHearingForStore = (appealId: string, hearing) => {
return {
appealId,
hearing: {
heldBy: hearing.held_by,
viewedByJudge: hearing.viewed_by_judge,
date: hearing.date,
type: hearing.type,
externalId: hearing.external_id,
disposition: hearing.disposition
}
};
};

export const prepareTasksForStore = (tasks: Array<Object>): Tasks =>
tasks.reduce((acc, task: Object): Tasks => {
const decisionPreparedBy = task.attributes.decision_prepared_by.first_name ? {
Expand Down
6 changes: 6 additions & 0 deletions client/constants/HEARING_DISPOSITION_TYPES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"held": "held",
"cancelled": "cancelled",
"postponed": "postponed",
"no_show": "no_show"
}
15 changes: 9 additions & 6 deletions client/test/karma/queue/ColocatedTaskListView-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,15 +148,17 @@ describe('ColocatedTaskListView', () => {

const cells = wrapper.find('td');

expect(cells).to.have.length(6);
expect(cells).to.have.length(7);
const wrappers = [];

for (let i = 0; i < cells.length; i++) {
wrappers.push(cells.at(i));
}
const [caseDetails, columnTasks, types, docketNumber, daysWaiting, documents] = wrappers;
const [hearings, caseDetails, columnTasks, types, docketNumber, daysWaiting, documents] = wrappers;
const task = taskNewAssigned;

expect(hearings.text()).to.include('');
expect(caseDetails.text()).to.include(appeal.veteranFullName);
expect(caseDetails.text()).to.include(appeal.veteranFullName);
expect(caseDetails.text()).to.include(appeal.veteranFileNumber);
expect(columnTasks.text()).to.include(CO_LOCATED_ADMIN_ACTIONS[task.label]);
Expand Down Expand Up @@ -226,14 +228,14 @@ describe('ColocatedTaskListView', () => {

const cells = wrapper.find('td');

expect(cells).to.have.length(6);
expect(cells).to.have.length(7);
const wrappers = [];

for (let i = 0; i < cells.length; i++) {
wrappers.push(cells.at(i));
}
{
const [daysOnHold, documents] = wrappers.slice(4);
const [daysOnHold, documents] = wrappers.slice(5);

expect(daysOnHold.text()).to.equal('1 of 30');
expect(documents.html()).to.include(`/reader/appeal/${taskWithNewDocs.externalAppealId}/documents`);
Expand Down Expand Up @@ -298,14 +300,15 @@ describe('ColocatedTaskListView', () => {

const cells = wrapper.find('td');

expect(cells).to.have.length(6);
expect(cells).to.have.length(7);
const wrappers = [];

for (let i = 0; i < cells.length; i++) {
wrappers.push(cells.at(i));
}
const [caseDetails, columnTasks, types, docketNumber, daysOnHold, documents] = wrappers;
const [hearings, caseDetails, columnTasks, types, docketNumber, daysOnHold, documents] = wrappers;

expect(hearings.text()).to.include('');
expect(caseDetails.text()).to.include(appeal.veteranFullName);
expect(caseDetails.text()).to.include(appeal.veteranFileNumber);
expect(columnTasks.text()).to.include(CO_LOCATED_ADMIN_ACTIONS[task.label]);
Expand Down
Loading