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

[TTAHUB-430] add approved and created date to activity report tables #616

Merged
merged 15 commits into from
Nov 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/src/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,5 @@ export const ESCAPE_KEY_CODE = 27;
export const ESCAPE_KEY_CODES = ['Escape', 'Esc'];

export const DATE_FMT = 'YYYY/MM/DD';
export const DATE_DISPLAY_FORMAT = 'MM/DD/YYYY';
export const EARLIEST_INC_FILTER_DATE = moment('2020-08-31');
35 changes: 20 additions & 15 deletions frontend/src/components/ActivityReportsTable/ReportRow.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Tag, Checkbox,
} from '@trussworks/react-uswds';
import { Checkbox } from '@trussworks/react-uswds';
import { Link, useHistory } from 'react-router-dom';

import moment from 'moment';
import ContextMenu from '../ContextMenu';
import { getReportsDownloadURL } from '../../fetchers/helpers';
import TooltipWithCollection from '../TooltipWithCollection';
import Tooltip from '../Tooltip';
import { DATE_DISPLAY_FORMAT } from '../../Constants';

function ReportRow({
report, openMenuUp, handleReportSelect, isChecked,
Expand All @@ -22,6 +22,8 @@ function ReportRow({
collaborators,
lastSaved,
calculatedStatus,
approvedAt,
createdAt,
legacyId,
} = report;

Expand Down Expand Up @@ -85,24 +87,21 @@ function ReportRow({
</td>
<td>{startDate}</td>
<td>
<span className="smart-hub--ellipsis" title={authorName}>
{authorName}
</span>
<Tooltip
displayText={authorName}
tooltipText={authorName}
buttonLabel="click to reveal author name"
/>
</td>
<td>{moment(createdAt).format(DATE_DISPLAY_FORMAT)}</td>
<td>
<TooltipWithCollection collection={topics} collectionTitle={`topics for ${displayId}`} />
</td>
<td>
<TooltipWithCollection collection={collaboratorNames} collectionTitle={`collaborators for ${displayId}`} />
</td>
<td>{lastSaved}</td>
<td>
<Tag
className={`smart-hub--table-tag-status smart-hub--status-${calculatedStatus}`}
>
{calculatedStatus === 'needs_action' ? 'Needs action' : calculatedStatus}
</Tag>
</td>
<td>{approvedAt && moment(approvedAt).format(DATE_DISPLAY_FORMAT)}</td>
<td>
<ContextMenu label={contextMenuLabel} menuItems={menuItems} up={openMenuUp} />
</td>
Expand All @@ -122,14 +121,20 @@ export const reportPropTypes = {
}),
}),
})).isRequired,
approvedAt: PropTypes.string,
createdAt: PropTypes.string,
startDate: PropTypes.string.isRequired,
author: PropTypes.shape({
fullName: PropTypes.string,
homeRegionId: PropTypes.number,
name: PropTypes.string,
}).isRequired,
topics: PropTypes.arrayOf(PropTypes.string).isRequired,
collaborators: PropTypes.arrayOf(PropTypes.string).isRequired,
collaborators: PropTypes.arrayOf(
PropTypes.shape({
fullName: PropTypes.string,
}),
).isRequired,
lastSaved: PropTypes.string.isRequired,
calculatedStatus: PropTypes.string.isRequired,
legacyId: PropTypes.string,
Expand Down
41 changes: 9 additions & 32 deletions frontend/src/components/ActivityReportsTable/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,28 +317,6 @@ describe('Table sorting', () => {
await screen.findByText('Activity Reports');
});

it('clicking status column header will sort by status', async () => {
const statusColumnHeader = await screen.findByText(/status/i);
fetchMock.reset();
fetchMock.get(
'/api/activity-reports?sortBy=calculatedStatus&sortDir=asc&offset=0&limit=10&region.in[]=1',
{ count: 2, rows: activityReportsSorted },
);

fireEvent.click(statusColumnHeader);
await waitFor(() => expect(screen.getAllByRole('cell')[7]).toHaveTextContent(/needs action/i));
await waitFor(() => expect(screen.getAllByRole('cell')[16]).toHaveTextContent(/draft/i));

fetchMock.get(
'/api/activity-reports?sortBy=calculatedStatus&sortDir=desc&offset=0&limit=10&region.in[]=1',
{ count: 2, rows: activityReports },
);

fireEvent.click(statusColumnHeader);
await waitFor(() => expect(screen.getAllByRole('cell')[7]).toHaveTextContent(/draft/i));
await waitFor(() => expect(screen.getAllByRole('cell')[16]).toHaveTextContent(/needs action/i));
});

it('clicking Last saved column header will sort by updatedAt', async () => {
const columnHeader = await screen.findByText(/last saved/i);

Expand All @@ -348,8 +326,8 @@ describe('Table sorting', () => {
);

fireEvent.click(columnHeader);
await waitFor(() => expect(screen.getAllByRole('cell')[6]).toHaveTextContent(/02\/04\/2021/i));
await waitFor(() => expect(screen.getAllByRole('cell')[15]).toHaveTextContent(/02\/05\/2021/i));
await waitFor(() => expect(screen.getAllByRole('cell')[7]).toHaveTextContent(/02\/04\/2021/i));
await waitFor(() => expect(screen.getAllByRole('cell')[17]).toHaveTextContent(/02\/05\/2021/i));
});

it('clicking Collaborators column header will sort by collaborators', async () => {
Expand All @@ -361,8 +339,8 @@ describe('Table sorting', () => {
);

await act(async () => fireEvent.click(columnHeader));
await waitFor(() => expect(screen.getAllByRole('cell')[5]).toHaveTextContent('Cucumber User, GS Hermione Granger, SS'));
await waitFor(() => expect(screen.getAllByRole('cell')[14]).toHaveTextContent('Orange, GS Hermione Granger, SS'));
await waitFor(() => expect(screen.getAllByRole('cell')[6]).toHaveTextContent('Cucumber User, GS Hermione Granger, SS'));
await waitFor(() => expect(screen.getAllByRole('cell')[16]).toHaveTextContent('Orange, GS Hermione Granger, SS'));
});

it('clicking Topics column header will sort by topics', async () => {
Expand All @@ -374,8 +352,7 @@ describe('Table sorting', () => {
);

await act(async () => fireEvent.click(columnHeader));
await waitFor(() => expect(screen.getAllByRole('cell')[4]).toHaveTextContent(''));
await waitFor(() => expect(screen.getAllByRole('cell')[13]).toHaveTextContent(/Behavioral \/ Mental Health CLASS: Instructional Support click to visually reveal the topics for R14-AR-1$/i));
await waitFor(() => expect(screen.getAllByRole('cell')[15]).toHaveTextContent(/Behavioral \/ Mental Health CLASS: Instructional Support click to visually reveal the topics for R14-AR-1$/i));
});

it('clicking Creator column header will sort by author', async () => {
Expand All @@ -388,7 +365,7 @@ describe('Table sorting', () => {

fireEvent.click(columnHeader);
await waitFor(() => expect(screen.getAllByRole('cell')[3]).toHaveTextContent('Kiwi, GS'));
await waitFor(() => expect(screen.getAllByRole('cell')[12]).toHaveTextContent('Kiwi, TTAC'));
await waitFor(() => expect(screen.getAllByRole('cell')[13]).toHaveTextContent('Kiwi, TTAC'));
});

it('clicking Start date column header will sort by start date', async () => {
Expand All @@ -401,7 +378,7 @@ describe('Table sorting', () => {

fireEvent.click(columnHeader);
await waitFor(() => expect(screen.getAllByRole('cell')[2]).toHaveTextContent('02/01/2021'));
await waitFor(() => expect(screen.getAllByRole('cell')[11]).toHaveTextContent('02/08/2021'));
await waitFor(() => expect(screen.getAllByRole('cell')[12]).toHaveTextContent('02/08/2021'));
});

it('clicking Grantee column header will sort by grantee', async () => {
Expand Down Expand Up @@ -462,8 +439,8 @@ describe('Table sorting', () => {
);

fireEvent.click(pageOne);
await waitFor(() => expect(screen.getAllByRole('cell')[6]).toHaveTextContent(/02\/05\/2021/i));
await waitFor(() => expect(screen.getAllByRole('cell')[15]).toHaveTextContent(/02\/04\/2021/i));
await waitFor(() => expect(screen.getAllByRole('cell')[7]).toHaveTextContent(/02\/05\/2021/i));
await waitFor(() => expect(screen.getAllByRole('cell')[17]).toHaveTextContent(/02\/04\/2021/i));
});

it('clicking on the second page updates to, from and total', async () => {
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/ActivityReportsTable/index.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.usa-table-container--scrollable {
.usa-table-container--scrollable,
.usa-checkbox__label {
margin-top: 0px;
}

3 changes: 2 additions & 1 deletion frontend/src/components/ActivityReportsTable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,10 +299,11 @@ function ActivityReportsTable({
{renderColumnHeader('Grantee', 'activityRecipients')}
{renderColumnHeader('Start date', 'startDate')}
{renderColumnHeader('Creator', 'author')}
{renderColumnHeader('Created date', 'createdAt')}
{renderColumnHeader('Topic(s)', 'topics')}
{renderColumnHeader('Collaborator(s)', 'collaborators')}
{renderColumnHeader('Last saved', 'updatedAt')}
{renderColumnHeader('Status', 'calculatedStatus')}
{renderColumnHeader('Approved date', 'approvedAt')}
<th scope="col" aria-label="context menu" />
</tr>
</thead>
Expand Down
16 changes: 7 additions & 9 deletions frontend/src/components/DatePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ import { SingleDatePicker } from 'react-dates';
import { OPEN_UP, OPEN_DOWN } from 'react-dates/constants';
import { Controller } from 'react-hook-form/dist/index.ie11';
import moment from 'moment';

import { DATE_DISPLAY_FORMAT } from '../Constants';
import './DatePicker.css';

const dateFmt = 'MM/DD/YYYY';

const DateInput = ({
control, minDate, name, disabled, maxDate, openUp, required, ariaName, maxDateInclusive,
}) => {
Expand All @@ -29,17 +27,17 @@ const DateInput = ({
const openDirection = openUp ? OPEN_UP : OPEN_DOWN;

const isOutsideRange = (date) => {
const isBefore = minDate && date.isBefore(moment(minDate, dateFmt));
const isBefore = minDate && date.isBefore(moment(minDate, DATE_DISPLAY_FORMAT));

// If max date is inclusive (maxDateInclusive == true)
// allow the user to pick a start date that is the same as the maxDate
// otherwise, only the day before is allowed
let isAfter = false;
if (maxDateInclusive) {
const newDate = moment(maxDate, dateFmt).add(1, 'days');
isAfter = maxDate && date.isAfter(newDate, dateFmt);
const newDate = moment(maxDate, DATE_DISPLAY_FORMAT).add(1, 'days');
isAfter = maxDate && date.isAfter(newDate, DATE_DISPLAY_FORMAT);
} else {
isAfter = maxDate && date.isAfter(moment(maxDate, dateFmt));
isAfter = maxDate && date.isAfter(moment(maxDate, DATE_DISPLAY_FORMAT));
}

return isBefore || isAfter;
Expand All @@ -52,7 +50,7 @@ const DateInput = ({
<div className="usa-hint font-body-2xs" id={hintId}>mm/dd/yyyy</div>
<Controller
render={({ onChange, value, ref }) => {
const date = value ? moment(value, dateFmt) : null;
const date = value ? moment(value, DATE_DISPLAY_FORMAT) : null;
return (
<div className="display-flex smart-hub--date-picker-input">
<SingleDatePicker
Expand All @@ -69,7 +67,7 @@ const DateInput = ({
disabled={disabled}
hideKeyboardShortcutsPanel
onDateChange={(d) => {
const newDate = d ? d.format(dateFmt) : d;
const newDate = d ? d.format(DATE_DISPLAY_FORMAT) : d;
onChange(newDate);
const input = document.getElementById(name);
if (input) input.focus();
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Tooltip.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
display: inline-block;
overflow-x: hidden;
overflow-y: visible;
width: 173.5px;
max-width: 175px;
text-overflow: ellipsis;
vertical-align: middle;
}
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/TooltipWithCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ export default function TooltipWithCollection({ collection, collectionTitle }) {

if (collection.length === 1) {
return (
<span className="smarthub-ellipsis">{tooltip}</span>
<Tooltip
displayText={tooltip}
tooltipText={tooltip}
buttonLabel={`click to visually reveal the ${collectionTitle}`}
/>
);
}

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/__tests__/TooltipWithCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ describe('TooltipWithCollection', () => {

it('renders a single span when passed a one item array', async () => {
renderTooltip(['Jimbo']);
const jimbo = screen.getByText('Jimbo');
const jimbo = screen.getAllByText('Jimbo')[1];
expect(jimbo).toBeVisible();
expect(jimbo.parentElement).toHaveClass('smarthub-ellipsis');
expect(jimbo.parentElement.parentElement).toHaveClass('smart-hub--ellipsis');
});
});
5 changes: 3 additions & 2 deletions frontend/src/pages/ApprovedActivityReport/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ViewTable from './components/ViewTable';
import { getReport, unlockReport } from '../../fetchers/activityReports';
import { allRegionsUserHasPermissionTo, canUnlockReports } from '../../permissions';
import Modal from '../../components/Modal';
import { DATE_DISPLAY_FORMAT } from '../../Constants';

/**
*
Expand Down Expand Up @@ -192,8 +193,8 @@ export default function ApprovedActivityReport({ match, user }) {
setParticipantCount(newCount);
setReasons(formatSimpleArray(report.reason));
setProgramType(formatSimpleArray(report.programTypes));
setStartDate(moment(report.startDate, 'MM/DD/YYYY').format('MMMM D, YYYY'));
setEndDate(moment(report.endDate, 'MM/DD/YYYY').format('MMMM D, YYYY'));
setStartDate(moment(report.startDate, DATE_DISPLAY_FORMAT).format('MMMM D, YYYY'));
setEndDate(moment(report.endDate, DATE_DISPLAY_FORMAT).format('MMMM D, YYYY'));
setDuration(`${report.duration} hours`);
setMethod(formatMethod(report.ttaType, report.virtualDeliveryType));
setRequester(formatRequester(report.requester));
Expand Down
19 changes: 15 additions & 4 deletions frontend/src/pages/Landing/MyAlerts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
Tag, Table, useModal, connectModal,
} from '@trussworks/react-uswds';
import { Link, useHistory } from 'react-router-dom';

import moment from 'moment';
import DeleteReportModal from '../../components/DeleteReportModal';
import Container from '../../components/Container';
import ContextMenu from '../../components/ContextMenu';
Expand Down Expand Up @@ -44,6 +44,7 @@ function ReportsRow({ reports, removeAlert, message }) {
calculatedStatus,
pendingApprovals,
approvers,
createdAt,
} = report;

const justSubmitted = message && message.reportId === id;
Expand Down Expand Up @@ -92,9 +93,18 @@ function ReportsRow({ reports, removeAlert, message }) {
</td>
<td>{startDate}</td>
<td>
<span className="smart-hub--ellipsis" title={author ? author.fullName : ''}>
{author ? author.fullName : ''}
</span>
{ author
? (
<Tooltip
displayText={author.fullName}
tooltipText={author.fullName}
buttonLabel={`click to reveal: ${author.fullName} `}
screenReadDisplayText={false}
/>
) : <span /> }
</td>
<td>
{moment(createdAt).format('MM/DD/YYYY')}
</td>
<td>
<TooltipWithCollection collection={collaboratorNames} collectionTitle={`collaborators for ${displayId}`} />
Expand Down Expand Up @@ -311,6 +321,7 @@ function MyAlerts(props) {
{renderColumnHeader('Grantee', 'activityRecipients')}
{renderColumnHeader('Start date', 'startDate')}
{renderColumnHeader('Creator', 'author')}
{renderColumnHeader('Created date', 'createdAt')}
{renderColumnHeader('Collaborator(s)', 'collaborators')}
{renderColumnHeader('Approvers(s)', 'approvals', true)}
{renderColumnHeader('Status', 'calculatedStatus')}
Expand Down
Loading