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

Display my alerts #170

Merged
merged 15 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from 9 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
6 changes: 6 additions & 0 deletions frontend/src/fetchers/activityReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { get, put, post } from './index';
import { DECIMAL_BASE } from '../Constants';

const activityReportUrl = join('/', 'api', 'activity-reports');
const activityReportAlertUrl = join('/', 'api', 'activity-reports', 'alerts');

export const getApprovers = async (region) => {
const res = await get(join(activityReportUrl, 'approvers', `?region=${region}`));
Expand Down Expand Up @@ -35,6 +36,11 @@ export const getReports = async () => {
return reports.json();
};

export const getReportAlerts = async () => {
const reports = await get(activityReportAlertUrl);
return reports.json();
};

export const getRecipients = async () => {
const recipients = await get(join(activityReportUrl, 'activity-recipients'));
return recipients.json();
Expand Down
146 changes: 146 additions & 0 deletions frontend/src/pages/Landing/MyAlerts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Tag, Table } from '@trussworks/react-uswds';
import { Link } from 'react-router-dom';
import SimpleBar from 'simplebar-react';
import 'simplebar/dist/simplebar.min.css';

import Container from '../../components/Container';
import NewReport from './NewReport';
import 'uswds/dist/css/uswds.css';
import '@trussworks/react-uswds/lib/index.css';
import './index.css';

function renderReports(reports) {
return reports.map((report) => {
const {
id,
displayId,
activityRecipients,
startDate,
author,
collaborators,
status,
} = report;

const recipientsTitle = activityRecipients.reduce(
(result, ar) => `${result + (ar.grant ? ar.grant.grantee.name : ar.name)}\n`,
'',
);

const recipients = activityRecipients.map((ar) => (
<Tag
key={ar.name.slice(1, 3) + ar.id}
className="smart-hub--table-collection"
>
{ar.grant ? ar.grant.grantee.name : ar.name}
</Tag>
));

const collaboratorsTitle = collaborators.reduce(
(result, collaborator) => `${result + collaborator.fullName}\n`,
'',
);

const collaboratorsWithTags = collaborators.map((collaborator) => (
<Tag
key={collaborator.fullName.slice(1, 13)}

Choose a reason for hiding this comment

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

Can we use collaborator.id here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed.

className="smart-hub--table-collection"
>
{collaborator.fullName}
</Tag>
));

return (
<tr key={`my_alerts_${id}`}>
<td>
<Link
to={`/activity-reports/${id}/activity-summary`}

Choose a reason for hiding this comment

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

If the /activity-summary is removed the user will be redirected to the review page if the report is not in draft mode (the report is waiting for approval or "Needs Action")

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated.

href={`/activity-reports/${id}/activity-summary`}
>
{displayId}
</Link>
</td>
<td>
<span className="smart-hub--ellipsis" title={recipientsTitle}>
{recipients}
</span>
</td>
<td>{startDate}</td>
<td>
<span className="smart-hub--ellipsis" title={author.fullName}>
{author.fullName}
</span>
</td>
<td>
<span className="smart-hub--ellipsis" title={collaboratorsTitle}>
{collaboratorsWithTags}
</span>
</td>
<td>
<Tag
className={`smart-hub--table-tag-status smart-hub--status-${status}`}
>
{status}
</Tag>
</td>
</tr>
);
});
}

function MyAlerts({ reports }) {
return (
<>
{ reports && reports.length === 0 && (
<Container className="landing" padding={0}>
<div id="caughtUp">
<div><h2>You&apos;re all caught up!</h2></div>
<p id="beginNew">Would you like to begin a new activity report?</p>
<NewReport />
</div>
</Container>
) }
{ reports && reports.length > 0 && (
<SimpleBar>
<Container className="landing inline-size" padding={0}>
<Table bordered={false}>
<caption className="smart-hub--table-caption">
My activity report alerts
</caption>
<thead>
<tr>
<th scope="col">Report ID</th>
<th
scope="col"
>
Grantee
</th>
<th scope="col">Start date</th>
<th
scope="col"
>
Creator
</th>
<th scope="col">Collaborator(s)</th>
<th
scope="col"
>
Status
</th>
</tr>
</thead>
<tbody>{renderReports(reports)}</tbody>
</Table>
</Container>
</SimpleBar>
)}
</>
);
}

MyAlerts.propTypes = {
reports: PropTypes.node.isRequired,
Copy link

Choose a reason for hiding this comment

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

I'm getting this in the console:

Warning: Failed prop type: Invalid prop reportssupplied toMyAlerts, expected a ReactNode

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed.

};

export default MyAlerts;
22 changes: 22 additions & 0 deletions frontend/src/pages/Landing/NewReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Link } from 'react-router-dom';

import 'uswds/dist/css/uswds.css';
import '@trussworks/react-uswds/lib/index.css';
import './index.css';

function NewReport() {
return (
<Link
to="/activity-reports/new"
referrerPolicy="same-origin"
className="usa-button smart-hub--new-report-btn"
variant="unstyled"
>
<span className="smart-hub--plus">+</span>
<span className="smart-hub--new-report">New Activity Report</span>
</Link>
);
}

export default NewReport;
86 changes: 86 additions & 0 deletions frontend/src/pages/Landing/__tests__/MyAlerts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import '@testing-library/jest-dom';
import React from 'react';
import {
render, screen,
} from '@testing-library/react';
import { MemoryRouter } from 'react-router';

import MyAlerts from '../MyAlerts';
import activityReports from '../mocks';

describe('My Alerts', () => {
beforeEach(() => {
render(
<MemoryRouter>
<MyAlerts reports={activityReports} />
</MemoryRouter>,
);
});

test('displays report id column', async () => {
const reportIdColumnHeader = await screen.findByRole('columnheader', {
name: /report id/i,
});
expect(reportIdColumnHeader).toBeVisible();
});

test('displays grantee column', async () => {
const granteeColumnHeader = await screen.findByRole('columnheader', {
name: /grantee/i,
});
expect(granteeColumnHeader).toBeVisible();
});

test('displays start date column', async () => {
const startDateColumnHeader = await screen.findByRole('columnheader', {
name: /start date/i,
});
expect(startDateColumnHeader).toBeVisible();
});

test('displays creator column', async () => {
const creatorColumnHeader = await screen.findByRole('columnheader', {
name: /creator/i,
});
expect(creatorColumnHeader).toBeVisible();
});

test('displays the correct grantees', async () => {
const grantees = await screen.findByRole('cell', {
name: /johnston-romaguera\njohnston-romaguera\ngrantee name/i,
});
const nonGrantees = await screen.findByRole('cell', {
name: /qris system/i,
});

expect(grantees).toBeVisible();
expect(nonGrantees).toBeVisible();
});

test('displays the correct start date', async () => {
const startDate = await screen.findByRole('cell', {
name: /02\/08\/2021/i,
});

expect(startDate).toBeVisible();
});

test('displays the correct collaborators', async () => {
const collaborators = await screen.findByRole('cell', {
name: /cucumber user, gs\nhermione granger, ss/i,
});

expect(collaborators).toBeVisible();
expect(collaborators.firstChild).toHaveClass('smart-hub--ellipsis');
expect(collaborators.firstChild.children.length).toBe(2);
expect(collaborators.firstChild.firstChild).toHaveClass('usa-tag smart-hub--table-collection');
expect(collaborators.firstChild.firstChild).toHaveTextContent('Cucumber User');
expect(collaborators.firstChild.lastChild).toHaveTextContent('Hermione Granger');
});

test('displays the correct statuses', async () => {
const statuses = await screen.findAllByText(/draft/i);

expect(statuses.length).toBe(2);
});
});
27 changes: 14 additions & 13 deletions frontend/src/pages/Landing/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import fetchMock from 'fetch-mock';
import UserContext from '../../../UserContext';
import Landing from '../index';
import activityReports from '../mocks';
import * as page from '../index';

describe('Landing Page', () => {
beforeEach(() => {
fetchMock.get('/api/activity-reports', activityReports);
fetchMock.get('/api/activity-reports/alerts', []);
const user = {
name: '[email protected]',
};
Expand Down Expand Up @@ -69,7 +69,7 @@ describe('Landing Page', () => {

test('displays the correct report id', async () => {
const reportIdLink = await screen.findByRole('link', {
name: /r14-000001/i,
name: /r14-ar-1/i,
});

expect(reportIdLink).toBeVisible();
Expand All @@ -80,15 +80,15 @@ describe('Landing Page', () => {
});

test('displays the correct grantees', async () => {
const grantees = await screen.findByRole('cell', {
const grantee = await screen.findByRole('cell', {
name: /johnston-romaguera\njohnston-romaguera\ngrantee name/i,
});
const nonGrantees = await screen.findByRole('cell', {
const nonGrantee = await screen.findByRole('cell', {
name: /qris system/i,
});

expect(grantees).toBeVisible();
expect(nonGrantees).toBeVisible();
expect(grantee).toBeVisible();
expect(nonGrantee).toBeVisible();
});

test('displays the correct start date', async () => {
Expand Down Expand Up @@ -138,24 +138,25 @@ describe('Landing Page', () => {
expect(optionButtons.length).toBe(2);
});

test('activityReportId is correct', () => {
const spy = jest.spyOn(page, 'activityReportId');
const reportId = page.activityReportId(333, 9);
test('displays the new activity report button', async () => {
const newActivityReportBtns = await screen.findAllByText(/New Activity Report/);

expect(spy).toHaveBeenCalled();
expect(reportId).toBe('R09-000333');

spy.mockRestore();
expect(newActivityReportBtns.length).toBe(1);
});
});

describe('Landing Page error', () => {
afterEach(() => fetchMock.restore());

beforeEach(() => {
fetchMock.get('/api/activity-reports/alerts', []);
});

it('handles errors by displaying an error message', async () => {
fetchMock.get('/api/activity-reports', 500);
render(<MemoryRouter><Landing authenticated /></MemoryRouter>);
const alert = await screen.findByRole('alert');
expect(alert).toBeVisible();
expect(alert).toHaveTextContent('Unable to fetch reports');
});

Expand Down
21 changes: 21 additions & 0 deletions frontend/src/pages/Landing/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
h1.landing {
font-size: 45px;
font-family: 'Times New Roman', Times, serif;
white-space: nowrap;
margin-right: 30px;
}

.landing .usa-table {
Expand Down Expand Up @@ -93,6 +95,11 @@ h1.landing {
color: #148439;
}

.smart-hub--status-needs_action {
background: #f9e0e4;
color: #d42240;
}

.smart-hub--status- {
background: #f8f8f8;
}
Expand Down Expand Up @@ -163,3 +170,17 @@ thead th.descending::after {
.simplebar-scrollbar:before {
background: #c5c5c5;
}

#caughtUp {
padding: 100px 320px;
white-space: nowrap;
}

#caughtUp h2 {
white-space: nowrap;
padding-left: 50px;
}

#beginNew {
padding-bottom: 15px;
}
Loading