Skip to content

Commit

Permalink
Merge pull request #2329 from HHS/al-ttahub-3316-3317-overview-widgets
Browse files Browse the repository at this point in the history
QA dashboard overview FE
  • Loading branch information
AdamAdHocTeam authored Oct 29, 2024
2 parents e3f7c84 + b71995f commit 52e07c5
Show file tree
Hide file tree
Showing 170 changed files with 10,506 additions and 1,765 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ parameters:
type: string
dev_git_branch: # change to feature branch to test deployment
description: "Name of github branch that will deploy to dev"
default: "al-ttahub-3329-fix-event-view"
default: "al-ttahub-3402-connect-be-overview"
type: string
sandbox_git_branch: # change to feature branch to test deployment
default: "mb/TTAHUB-3483/checkbox-to-activity-reports"
Expand Down
8 changes: 5 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,15 @@
"<rootDir>/src/pages/RegionalDashboard/constants.js",
"<rootDir>/src/pages/makecolors.js",
"<rootDir>/src/NetworkContext.js",
"<rootDir>/src/hooks/helpers.js"
"<rootDir>/src/hooks/helpers.js",
"<rootDir>/src/testHelpers.js",
"<rootDir>/src/pages/activityReports/testHelpers.js"
],
"coverageThreshold": {
"global": {
"statements": 90,
"functions": 89,
"branches": 89,
"functions": 90,
"branches": 90,
"lines": 90
}
}
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ export const ESCAPE_KEY_CODE = 27;
export const GOALS_PER_PAGE = 10;
export const TOPICS_PER_PAGE = 10;
export const COURSES_PER_PAGE = 10;
export const RECIPIENTS_WITH_NO_TTA_PER_PAGE = 10;
export const RECIPIENTS_WITH_OHS_STANDARD_FEI_GOAL_PER_PAGE = 10;
export const RECIPIENTS_WITH_CLASS_SCORES_AND_GOALS_GOAL_PER_PAGE = 10;

// In Internet Explorer (tested on release 9 and 11) and Firefox 36 and earlier
// the Esc key returns "Esc" instead of "Escape".
Expand All @@ -176,3 +179,24 @@ export const LOCAL_STORAGE_ADDITIONAL_DATA_KEY = (id) => `ar-additional-data-${i
export const LOCAL_STORAGE_EDITABLE_KEY = (id) => `ar-can-edit-${id}-${LOCAL_STORAGE_CACHE_NUMBER}`;
export const SESSION_STORAGE_IMPERSONATION_KEY = `auth-impersonation-id-${LOCAL_STORAGE_CACHE_NUMBER}`;
export const REGIONAL_RESOURCE_DASHBOARD_FILTER_KEY = 'regional-resources-dashboard-filters';

export const SUPPORT_LINK = 'https://app.smartsheetgov.com/b/form/f0b4725683f04f349a939bd2e3f5425a';
export const mustBeQuarterHalfOrWhole = (value) => {
if (value % 0.25 !== 0) {
return 'Duration must be rounded to the nearest quarter hour';
}
return true;
};

export const parseCheckboxEvent = (event) => {
const { target: { checked = null, value = null } = {} } = event;
return {
checked,
value,
};
};

export const arrayExistsAndHasLength = (array) => array && Array.isArray(array) && array.length > 0;

export const NOOP = () => {};
export const EMPTY_ARRAY = [];
30 changes: 30 additions & 0 deletions frontend/src/Routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ import SessionForm from './pages/SessionForm';
import ViewTrainingReport from './pages/ViewTrainingReport';
import QADashboard from './pages/QADashboard';
import SomethingWentWrong from './components/SomethingWentWrong';
import RecipientsWithNoTta from './pages/QADashboard/RecipientsWithNoTta';
import RecipientsWithClassScoresAndGoals from './pages/QADashboard/RecipientsWithClassScoresAndGoals';
import RecipientsWithOhsStandardFeiGoal from './pages/QADashboard/RecipientsWithOhsStandardFeiGoal';

export default function Routes({
alert,
Expand Down Expand Up @@ -157,6 +160,33 @@ export default function Routes({
</AppWrapper>
)}
/>
<Route
exact
path="/dashboards/qa-dashboard/recipients-with-no-tta"
render={() => (
<AppWrapper authenticated logout={logout}>
<RecipientsWithNoTta />
</AppWrapper>
)}
/>
<Route
exact
path="/dashboards/qa-dashboard/recipients-with-ohs-standard-fei-goal"
render={() => (
<AppWrapper authenticated logout={logout}>
<RecipientsWithOhsStandardFeiGoal />
</AppWrapper>
)}
/>
<Route
exact
path="/dashboards/qa-dashboard/recipients-with-class-scores-and-goals"
render={() => (
<AppWrapper authenticated logout={logout}>
<RecipientsWithClassScoresAndGoals />
</AppWrapper>
)}
/>
<Route
exact
path="/training-reports/:status(not-started|in-progress|complete|suspended)"
Expand Down
52 changes: 52 additions & 0 deletions frontend/src/__tests__/Constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { parseCheckboxEvent, NOOP, arrayExistsAndHasLength } from '../Constants';

describe('Constants', () => {
describe('NOOP', () => {
it('returns undefined', () => {
expect(NOOP()).toBeUndefined();
});
});

describe('arrayExistsAndHasLength', () => {
it('returns true if array exists and has length', () => {
expect(arrayExistsAndHasLength([1])).toBeTruthy();
});

it('returns false if array does not exist', () => {
expect(arrayExistsAndHasLength(null)).toBeFalsy();
});

it('returns false if array is not an array', () => {
expect(arrayExistsAndHasLength('string')).toBeFalsy();
});

it('returns false if array has no length', () => {
expect(arrayExistsAndHasLength([])).toBeFalsy();
});
});

describe('parseCheckboxEvent', () => {
it('returns checked and value from event', () => {
const event = {
target: {
checked: true,
value: 'shoes',
},
};
expect(parseCheckboxEvent(event)).toEqual({
checked: true,
value: 'shoes',
});
});

it('returns null checked and value from event', () => {
const event = {
target: {},
};
expect(parseCheckboxEvent(event)).toEqual({
checked: null,
value: null,
});
});
});
});
38 changes: 38 additions & 0 deletions frontend/src/__tests__/permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import isAdmin, {
canChangeGoalStatus,
canEditOrCreateGoals,
hasTrainingReportWritePermissions,
canEditOrCreateSessionReports,
} from '../permissions';

describe('permissions', () => {
Expand Down Expand Up @@ -415,4 +416,41 @@ describe('permissions', () => {
expect(hasTrainingReportWritePermissions(user)).toBeFalsy();
});
});

describe('canEditOrCreateSessionReports', () => {
it('returns true if the user is an admin', () => {
const user = {
permissions: [
{
scopeId: SCOPE_IDS.ADMIN,
},
],
};
expect(canEditOrCreateSessionReports(user, 1)).toBeTruthy();
});

it('returns true if the user has read_write_training_reports', () => {
const user = {
permissions: [
{
scopeId: SCOPE_IDS.READ_WRITE_TRAINING_REPORTS,
regionId: 1,
},
],
};
expect(canEditOrCreateSessionReports(user, 1)).toBeTruthy();
});

it('returns false otherwise', () => {
const user = {
permissions: [
{
scopeId: SCOPE_IDS.READ_REPORTS,
regionId: 1,
},
],
};
expect(canEditOrCreateSessionReports(user, 1)).toBeFalsy();
});
});
});
26 changes: 26 additions & 0 deletions frontend/src/__tests__/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,32 @@ describe('filtersToQueryString', () => {
const str = filtersToQueryString(filters);
expect(str).toBe(`region.in[]=14&startDate.win=${encodeURIComponent('2021/11/13-2021/12/13')}`);
});

it('handles region, second param', () => {
const filters = [
{
id: '07bc65ed-a4ce-410f-b7be-f685bc8921ed',
topic: 'startDate',
condition: 'is within',
query: '2021/11/13-2021/12/13',
},
];
const str = filtersToQueryString(filters, '14');
expect(str).toBe(`startDate.win=${encodeURIComponent('2021/11/13-2021/12/13')}&region.in[]=14`);
});

it('handles oddball region', () => {
const filters = [
{
id: '07bc65ed-a4ce-410f-b7be-f685bc8921ed',
topic: 'startDate',
condition: 'is within',
query: '2021/11/13-2021/12/13',
},
];
const str = filtersToQueryString(filters, 'YOLO');
expect(str).toBe(`startDate.win=${encodeURIComponent('2021/11/13-2021/12/13')}`);
});
});

describe('formatDateRange', () => {
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/components/ActivityReportsTable/ColumnHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
import React from 'react';
import PropTypes from 'prop-types';

export const getClassNamesFor = (n, sortBy, sortDirection) => (sortBy === n ? sortDirection : '');

function ColumnHeader({
displayName, name, sortBy, sortDirection, onUpdateSort,
displayName,
name,
sortBy,
sortDirection,
onUpdateSort,
}) {
const getClassNamesFor = (n) => (sortBy === n ? sortDirection : '');
const sortClassName = getClassNamesFor(name);
const sortClassName = getClassNamesFor(name, sortBy, sortDirection);
let fullAriaSort;
switch (sortClassName) {
case 'asc':
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/ActivityReportsTable/ReportsTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import Container from '../Container';
import TableHeader from '../TableHeader';
import ReportRow from './ReportRow';
import { REPORTS_PER_PAGE } from '../../Constants';
import { parseCheckboxEvent, REPORTS_PER_PAGE } from '../../Constants';
import './ReportsTable.css';

export default function ReportsTable({
Expand Down Expand Up @@ -51,7 +51,7 @@ export default function ReportsTable({

// The all-reports checkbox can select/deselect all visible reports
const toggleSelectAll = (event) => {
const { target: { checked = null } = {} } = event;
const { checked } = parseCheckboxEvent(event);

if (checked === true) {
setReportCheckboxes(makeReportCheckboxes(reports, true));
Expand All @@ -63,7 +63,7 @@ export default function ReportsTable({
};

const handleReportSelect = (event) => {
const { target: { checked = null, value = null } = {} } = event;
const { checked, value } = parseCheckboxEvent(event);
if (checked === true) {
setReportCheckboxes({ ...reportCheckboxes, [value]: true });
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,23 @@ import {
render, screen, act,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ColumnHeader from '../ColumnHeader';
import ColumnHeader, { getClassNamesFor } from '../ColumnHeader';

describe('ActivityReportsTable ColumnHeader', () => {
describe('getClassNamesFor', () => {
it('returns empty string when sortBy does not match', () => {
expect(getClassNamesFor('shoes', 'hats', 'asc')).toBe('');
});

it('returns asc when sortBy matches and sortDirection is asc', () => {
expect(getClassNamesFor('shoes', 'shoes', 'asc')).toBe('asc');
});

it('returns desc when sortBy matches and sortDirection is desc', () => {
expect(getClassNamesFor('shoes', 'shoes', 'desc')).toBe('desc');
});
});

const renderColumnHeader = (onUpdateSort = jest.fn(), sortDirection = 'asc') => {
const name = 'fanciest shoes';
render(
Expand Down
52 changes: 52 additions & 0 deletions frontend/src/components/ClassScoreBadge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import moment from 'moment';
import './ClassScoreBadge.scss';

const BadgeAbove = (fontSize) => (
<span className={`ttahub-badge--success ${fontSize} text-white text-bold`}>
Above all thresholds
</span>
);

const BadgeBelowQuality = (fontSize) => (
<span className={`ttahub-badge--warning ${fontSize} text-bold`}>
Below quality
</span>
);

const BadgeBelowCompetitive = (fontSize) => (
<span className={`ttahub-badge--error ${fontSize} text-white text-bold`}>
Below competitive
</span>
);

export function getScoreBadge(key, score, received, size) {
const fontSize = size || 'font-sans-2xs';
if (key === 'ES' || key === 'CO') {
if (score >= 6) return BadgeAbove(fontSize);
if (score < 5) return BadgeBelowCompetitive(fontSize);
return BadgeBelowQuality(fontSize);
}

if (key === 'IS') {
if (score >= 3) return BadgeAbove(fontSize);

// IS is slightly more complicated.
// See TTAHUB-2097 for details.
const dt = moment(received, 'MM/DD/YYYY');

if (dt.isAfter('2025-08-01')) {
if (score < 2.5) return BadgeBelowCompetitive(fontSize);
return BadgeBelowQuality(fontSize);
}

if (dt.isAfter('2020-11-09') && dt.isBefore('2025-07-31')) {
if (score < 2.3) return BadgeBelowCompetitive(fontSize);
return BadgeBelowQuality(fontSize);
}
}

return null;
}

export default getScoreBadge;
21 changes: 21 additions & 0 deletions frontend/src/components/ClassScoreBadge.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@use '../colors.scss' as *;

%badge {
background-color: $success-darkest;
border-radius: 12px;
padding: 4px 12px;
}

.ttahub-badge--success {
@extend %badge;
}

.ttahub-badge--warning {
background-color: $warning;
@extend %badge;
}

.ttahub-badge--error {
background-color: $error;
@extend %badge;
}
1 change: 0 additions & 1 deletion frontend/src/components/ContentFromFeedByTag.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export default function ContentFromFeedByTag({
}, [tagName, contentSelector]);

const classNames = `${className} ttahub-single-feed-item--by-tag ${contentSelector ? 'ttahub-single-feed-item--by-tag--with-selector' : ''}`;

return (
<div className={classNames}>
<FeedArticle title="" content={content} unread={false} key={content} openLinksInNewTab={openLinksInNewTab} partial />
Expand Down
Loading

0 comments on commit 52e07c5

Please sign in to comment.