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 12 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
150 changes: 150 additions & 0 deletions frontend/src/pages/Landing/MyAlerts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
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.id}
className="smart-hub--table-collection"
>
{collaborator.fullName}
</Tag>
));

return (
<tr key={`my_alerts_${id}`}>
<td>
<Link
to={`/activity-reports/${id}`}
href={`/activity-reports/${id}`}
>
{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 === 'Needs_action' ? 'Needs action' : 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.arrayOf(PropTypes.object),
};

MyAlerts.defaultProps = {
reports: [],
};

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
23 changes: 22 additions & 1 deletion frontend/src/pages/Landing/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
h1.landing {
font-size: 45px;
font-family: 'Times New Roman', Times, serif;
white-space: nowrap;
margin-right: 30px;
}

.landing .usa-table {
line-height: 1.5;
line-height: 2;
}

.landing .usa-table caption {
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