Skip to content

Commit

Permalink
Merge pull request #2424 from HHS/mb/TTAHUB-3484/insert-standard-goals
Browse files Browse the repository at this point in the history
[TTAHUB-3484] Insert standard goals
  • Loading branch information
thewatermethod authored Oct 31, 2024
2 parents 487c521 + 1cbe552 commit 1135d96
Show file tree
Hide file tree
Showing 26 changed files with 665 additions and 144 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: "kw-unsafe-inline"
default: "mb/TTAHUB-3484/insert-standard-goals"
type: string
sandbox_git_branch: # change to feature branch to test deployment
default: "mb/TTAHUB-3478/goal-nudge-version-2"
Expand Down
21 changes: 15 additions & 6 deletions frontend/src/components/GoalForm/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function Form({
isOnApprovedReport,
isOnReport,
isCurated,
isSourceEditable,
status,
datePickerKey,
fetchError,
Expand Down Expand Up @@ -155,12 +156,18 @@ export default function Form({
/>

<FormFieldThatIsSometimesReadOnly
permissions={[
!isCurated,
status !== 'Closed',
userCanEdit,
!isOnApprovedReport,
]}
permissions={
isCurated ? [
isSourceEditable,
status !== 'Closed',
userCanEdit,
!isOnApprovedReport,
] : [
status !== 'Closed',
userCanEdit,
!isOnApprovedReport,
]
}
label="Goal source"
value={uniq(Object.values(source || {})).join(', ') || ''}
>
Expand Down Expand Up @@ -290,11 +297,13 @@ Form.propTypes = {
})).isRequired,
goalTemplateId: PropTypes.number,
isReopenedGoal: PropTypes.bool.isRequired,
isSourceEditable: PropTypes.bool,
};

Form.defaultProps = {
endDate: null,
userCanEdit: false,
isCurated: false,
isSourceEditable: true,
goalTemplateId: null,
};
5 changes: 5 additions & 0 deletions frontend/src/components/GoalForm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default function GoalForm({
createdVia: '',
goalTemplateId: null,
isReopenedGoal: false,
isSourceEditable: true,
}), [possibleGrants]);

const [showForm, setShowForm] = useState(true);
Expand All @@ -76,6 +77,7 @@ export default function GoalForm({
const [source, setSource] = useState(grantsToMultiValue(goalDefaults.grants));
const [createdVia, setCreatedVia] = useState('');
const [isCurated, setIsCurated] = useState(goalDefaults.isCurated);
const [isSourceEditable, setIsSourceEditable] = useState(goalDefaults.isSourceEditable);
const [goalTemplateId, setGoalTemplateId] = useState(goalDefaults.goalTemplateId);
const [selectedGrants, setSelectedGrants] = useState(goalDefaults.grants);
const [goalOnApprovedAR, setGoalOnApprovedReport] = useState(goalDefaults.onApprovedAR);
Expand Down Expand Up @@ -142,6 +144,7 @@ export default function GoalForm({
setGoalOnApprovedReport(goal.onApprovedAR);
setGoalonAR(goal.onAR);
setIsCurated(goal.isCurated);
setIsSourceEditable(goal.isSourceEditable);
setGoalTemplateId(goal.goalTemplateId);
setSource(grantsToMultiValue(selectedGoalGrants, goal.source, ''));
setCreatedVia(goal.createdVia || '');
Expand Down Expand Up @@ -603,6 +606,7 @@ export default function GoalForm({
setGoalNumbers(goal.goalNumbers);
setSelectedGrants(goal.grants);
setIsCurated(goal.isCurated);
setIsSourceEditable(goal.isSourceEditable);
setPrompts(goal.prompts);
setSource(goal.source);
setCreatedVia(goal.createdVia);
Expand Down Expand Up @@ -709,6 +713,7 @@ export default function GoalForm({
isOnReport={goalOnAR}
isOnApprovedReport={goalOnApprovedAR}
isCurated={isCurated}
isSourceEditable={isSourceEditable}
status={status || 'Needs status'}
goalNumbers={goalNumbers}
userCanEdit={canEdit}
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/fetchers/__tests__/goalTemplates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import fetchMock from 'fetch-mock';
import join from 'url-join';
import {
getGoalTemplateSource,
} from '../goalTemplates';

const goalTemplatesUrl = join('/', 'api', 'goal-templates');

describe('goalTemplates fetcher', () => {
describe('getGoalTemplateSource', () => {
afterEach(() => fetchMock.reset());
it('should fetch goal template source', async () => {
fetchMock.get(join(goalTemplatesUrl, '1', 'source', '?grantIds=1'), { source: 'source' });
const source = await getGoalTemplateSource(1, [1]);

expect(source).toEqual({ source: 'source' });
});
});
});
7 changes: 7 additions & 0 deletions frontend/src/fetchers/goalTemplates.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ export async function getGoalTemplates(grantIds) {
return response.json();
}

export async function getGoalTemplateSource(templateId, grantIds) {
const params = grantIds.map((goalId) => `grantIds=${goalId}`).join('&');
const url = join(goalTemplatesUrl, String(templateId), 'source', `?${params}`);
const response = await get(url);
return response.json();
}

export async function getGoalTemplatePrompts(templateId, goalIds = []) {
const params = goalIds.map((goalId) => `goalIds=${goalId}`).join('&');
const url = join(goalTemplatesUrl, String(templateId), 'prompts', `?${params}`);
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/pages/ActivityReport/Pages/components/GoalForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export default function GoalForm({

const prompts = combinePrompts(templatePrompts, goal.prompts);
const isCurated = goal.isCurated || false;
const { isSourceEditable } = goal;

return (
<>
Expand Down Expand Up @@ -180,10 +181,10 @@ export default function GoalForm({
/>

<FormFieldThatIsSometimesReadOnly
permissions={[
!isCurated,
permissions={isCurated ? [
isSourceEditable,
!goal.onApprovedAR,
]}
] : [!goal.onApprovedAR]}
label="Goal source"
value={goalSource}
>
Expand All @@ -195,8 +196,6 @@ export default function GoalForm({
inputName={goalSourceInputName}
goalStatus={status}
isLoading={isAppLoading}
userCanEdit={!isCurated}
isOnReport={false}
isMultiRecipientGoal={isMultiRecipientReport}
/>
</FormFieldThatIsSometimesReadOnly>
Expand Down Expand Up @@ -238,6 +237,7 @@ GoalForm.propTypes = {
endDate: PropTypes.string,
isNew: PropTypes.bool,
isCurated: PropTypes.bool,
isSourceEditable: PropTypes.bool,
onApprovedAR: PropTypes.bool,
status: PropTypes.string,
source: PropTypes.string,
Expand Down
36 changes: 25 additions & 11 deletions frontend/src/pages/ActivityReport/Pages/components/GoalPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Label, Button, Checkbox } from '@trussworks/react-uswds';
import { useFormContext, useWatch, useController } from 'react-hook-form';
import Select from 'react-select';
import { getTopics } from '../../../../fetchers/topics';
import { getGoalTemplatePrompts } from '../../../../fetchers/goalTemplates';
import { getGoalTemplatePrompts, getGoalTemplateSource } from '../../../../fetchers/goalTemplates';
import Req from '../../../../components/Req';
import Option from './GoalOption';
import SingleValue from './GoalValue';
Expand Down Expand Up @@ -131,13 +131,30 @@ const GoalPicker = ({
];

const onChangeGoal = async (goal) => {
onChange(goal);
if (goal.isCurated) {
const prompts = await getGoalTemplatePrompts(goal.goalTemplateId, goal.goalIds);
if (prompts) {
setTemplatePrompts(prompts);
try {
if (goal.isCurated) {
const [prompts, source] = await Promise.all([
getGoalTemplatePrompts(goal.goalTemplateId, goal.goalIds),
// eslint-disable-next-line max-len
getGoalTemplateSource(goal.goalTemplateId, activityRecipients.map((ar) => ar.activityRecipientId)),
]);

onChange({
...goal,
source: source.source,
});

if (prompts) {
setTemplatePrompts(prompts);
}
} else {
onChange(goal);
setTemplatePrompts(false);
}
} else {

setSelectedGoal(null);
} catch (err) {
onChange(goal);
setTemplatePrompts(false);
}

Expand All @@ -147,8 +164,6 @@ const GoalPicker = ({
if (goal.goalIds) {
setDatePickerKey(`DPKEY-${goal.goalIds.join('-')}`);
}

setSelectedGoal(null);
};

const onKeep = async () => {
Expand Down Expand Up @@ -182,7 +197,6 @@ const GoalPicker = ({
onChangeGoal(goal);
};

const pickerLabel = useOhsStandardGoal ? 'Select OHS standard goal' : 'Select recipient\'s goal';
const pickerOptions = useOhsStandardGoal ? goalTemplates : options;

return (
Expand All @@ -204,7 +218,7 @@ const GoalPicker = ({
</Modal>
<div className="margin-top-3 position-relative">
<Label>
{pickerLabel}
Select recipient&apos;s goal
<Req />
<Select
name="goalForEditing"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const GP = ({ availableGoals, selectedGoals, goalForEditing, goalTemplates }) =>
role: 'central office',
},
collaborators: [],
activityRecipients: [{ activityRecipientId: 1 }],
},
});

Expand Down Expand Up @@ -254,9 +255,6 @@ describe('GoalPicker', () => {
});

selector = screen.queryByLabelText(/Select recipient's goal*/i);
expect(selector).toBeNull();

selector = await screen.findByLabelText(/Select ohs standard goal/i);

fireEvent.focus(selector);
fireEvent.keyDown(selector, {
Expand All @@ -283,6 +281,10 @@ describe('GoalPicker', () => {
prompt: 'WHYYYYYYYY?',
},
]);
fetchMock.get('/api/goal-templates/1/source?grantIds=1', {
source: 'source',
});

const availableGoals = [{
label: 'Goal 1',
value: 1,
Expand All @@ -291,17 +293,25 @@ describe('GoalPicker', () => {
goalTemplateId: 1,
}];

renderGoalPicker(availableGoals, null);
act(() => {
renderGoalPicker(availableGoals, null);
});

const selector = await screen.findByLabelText(/Select recipient's goal*/i);
const [availableGoal] = availableGoals;

await selectEvent.select(selector, [availableGoal.label]);
await act(async () => {
await selectEvent.select(selector, [availableGoal.label]);
});

const input = document.querySelector('[name="goalForEditing"]');
expect(input.value).toBe(availableGoal.value.toString());
});
it('with prompts', async () => {
fetchMock.get('/api/goal-templates/1/source?grantIds=1', {
source: 'source',
});

fetchMock.get('/api/goal-templates/1/prompts?goalIds=1', [
{
type: 'multiselect',
Expand All @@ -321,12 +331,16 @@ describe('GoalPicker', () => {
goalTemplateId: 1,
}];

renderGoalPicker(availableGoals, null);
act(() => {
renderGoalPicker(availableGoals, null);
});

const selector = await screen.findByLabelText(/Select recipient's goal*/i);
const [availableGoal] = availableGoals;

await selectEvent.select(selector, [availableGoal.label]);
await act(async () => {
await selectEvent.select(selector, [availableGoal.label]);
});

const input = document.querySelector('[name="goalForEditing"]');
expect(input.value).toBe(availableGoal.value.toString());
Expand Down
5 changes: 5 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,13 @@ const GOAL_CREATED_VIA = ['imported', 'activityReport', 'rtr', 'merge', 'admin']

const CURRENT_GOAL_SIMILARITY_VERSION = 4;

const FEI_PROD_GOAL_TEMPLATE_ID = 19017;
const CLASS_MONITORING_PROD_GOAL_TEMPLATE_ID = 18172;

module.exports = {
CURRENT_GOAL_SIMILARITY_VERSION,
FEI_PROD_GOAL_TEMPLATE_ID,
CLASS_MONITORING_PROD_GOAL_TEMPLATE_ID,
FILE_STATUSES,
IMPORT_STATUSES,
IMPORT_DATA_STATUSES,
Expand Down
Loading

0 comments on commit 1135d96

Please sign in to comment.