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

[Prod] Reopen closed goals, properly match goals with no collaborators, fix bug with goal similarity versioning #2112

Merged
merged 56 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
96fbf2d
Fix bug
thewatermethod Apr 9, 2024
de40307
Remove unused variables
thewatermethod Apr 11, 2024
1ae3abc
Merge remote-tracking branch 'origin/main' into mb/TTAHUB-2790/fix-si…
thewatermethod Apr 11, 2024
22fcc15
Add test and additional func.
thewatermethod Apr 11, 2024
6f89715
Fix logic again
thewatermethod Apr 11, 2024
af47217
Merge branch 'main' into mb/TTAHUB-2790/fix-similarity-group-closed-m…
thewatermethod Apr 11, 2024
dd5ccb2
is this it?
nvms Apr 15, 2024
527f8e6
Merge branch 'jp/2682/closeSuspendReasonContext' into jp/2683/useNewH…
nvms Apr 15, 2024
0f83452
missed the param
nvms Apr 15, 2024
4f2b2e0
Merge branch 'jp/2682/closeSuspendReasonContext' into jp/2683/useNewH…
nvms Apr 15, 2024
d134dc7
hooks..
nvms Apr 15, 2024
f6ce6d4
update tests and objective hook
nvms Apr 15, 2024
7ed27eb
Merge branch 'jp/2682/closeSuspendReasonContext' into jp/2683/useNewH…
nvms Apr 15, 2024
86402fc
coverage
nvms Apr 15, 2024
b70ebc2
adjust test
nvms Apr 15, 2024
29a1f94
coverage, change test
nvms Apr 15, 2024
633f010
squash a whole bunch of trial and error insanity
nvms Apr 15, 2024
d100bfb
remove log statements
nvms Apr 16, 2024
6d570ad
use helper in importPlanGoals
nvms Apr 16, 2024
f1dcc05
Fix folding
kryswisnaskas Apr 17, 2024
bb6a265
Fix lint errors
kryswisnaskas Apr 17, 2024
b5b1b6b
Adjust tests
kryswisnaskas Apr 17, 2024
16d0baf
update constants
nvms Apr 17, 2024
1ea6001
add fetcher
nvms Apr 17, 2024
6010c5e
add route & handler
nvms Apr 17, 2024
dda4aad
add reopen menuitem to GoalCard
nvms Apr 17, 2024
9108d23
add ReopenReasonModal
nvms Apr 17, 2024
e74bc1f
bump common package
nvms Apr 17, 2024
3d03340
bump package.json and yarn.lock
nvms Apr 17, 2024
696435a
give this a different label
nvms Apr 17, 2024
2b24cee
update labels
nvms Apr 17, 2024
fd8a298
bump be common
nvms Apr 17, 2024
7c37c29
test new modal
nvms Apr 17, 2024
1a3f6d5
push to sandbox
nvms Apr 18, 2024
426b51c
add procfile
nvms Apr 18, 2024
642d261
show -R on reopened goals in the UI
nvms Apr 18, 2024
6841bf3
better way to determine if its a reopened goal
nvms Apr 18, 2024
7e82c67
refactor aro cleanup
nvms Apr 18, 2024
775cb75
Merge branch 'jp/2683/useNewHelper' into jp/2793/reopen-frontend
nvms Apr 18, 2024
ae2ec1a
set isReopenedGoal on reopen
nvms Apr 18, 2024
2e5e01a
add -R to reopened goals on edit page
nvms Apr 18, 2024
58402bb
missed this status change
nvms Apr 18, 2024
39d75ce
Merge branch 'jp/2683/useNewHelper' into jp/2793/reopen-frontend
nvms Apr 18, 2024
df93b8d
update api tests
nvms Apr 18, 2024
fd21db2
update api tests
nvms Apr 18, 2024
ce8454d
forceAction
nvms Apr 19, 2024
756070b
a11y change per Matt, update test
nvms Apr 19, 2024
f7be8c0
Update test
thewatermethod Apr 19, 2024
c7b3c1e
Merge pull request #2103 from HHS/jp/2793/reopen-frontend
nvms Apr 19, 2024
23a6f0d
fix order of removal
nvms Apr 19, 2024
ea31a7e
Revert "Update test"
thewatermethod Apr 19, 2024
99fc827
Merge pull request #2101 from HHS/jp/2683/useNewHelper
nvms Apr 19, 2024
410ab8b
do not deploy to sandbox
kryswisnaskas Apr 19, 2024
471f4f1
Add a test
kryswisnaskas Apr 22, 2024
b969e33
Merge pull request #2104 from HHS/kw-no-identical-merge
kryswisnaskas Apr 23, 2024
9785220
Merge pull request #2086 from HHS/mb/TTAHUB-2790/fix-similarity-group…
thewatermethod Apr 23, 2024
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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ parameters:
default: "al/ttahub-2570/flat-resource-sql"
type: string
sandbox_git_branch: # change to feature branch to test deployment
default: "mb/TTAHUB/frontend-for-tr-dashboard"
default: "jp/2793/reopen-frontend"
type: string
prod_new_relic_app_id:
default: "877570491"
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"@hookform/error-message": "^0.0.5",
"@react-hook/resize-observer": "^1.2.6",
"@trussworks/react-uswds": "4.1.1",
"@ttahub/common": "2.0.18",
"@ttahub/common": "^2.1.3",
"@use-it/interval": "^1.0.0",
"async": "^3.2.3",
"browserslist": "^4.16.5",
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/components/GoalCards/GoalCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function GoalCard({
recipientId,
regionId,
showCloseSuspendGoalModal,
showReopenGoalModal,
performGoalStatusUpdate,
handleGoalCheckboxSelect,
isChecked,
Expand All @@ -77,6 +78,7 @@ function GoalCard({
createdVia,
collaborators,
onAR,
isReopenedGoal,
} = goal;

const sortedObjectives = [...objectives, ...(sessionObjectives || [])];
Expand All @@ -88,7 +90,7 @@ function GoalCard({
const lastTTA = useMemo(() => objectives.reduce((prev, curr) => (new Date(prev) > new Date(curr.endDate) ? prev : curr.endDate), ''), [objectives]);
const history = useHistory();

const goalNumbers = goal.goalNumbers.join(', ');
const goalNumbers = `${goal.goalNumbers.join(', ')}${isReopenedGoal ? '-R' : ''}`;

const { user } = useContext(UserContext);
const { setIsAppLoading } = useContext(AppLoadingContext);
Expand All @@ -110,6 +112,12 @@ function GoalCard({

const contextMenuLabel = `Actions for goal ${id}`;
const menuItems = [
...(goalStatus === 'Closed' ? [{
label: 'Reopen',
onClick: () => {
showReopenGoalModal(id);
},
}] : []),
{
label: goalStatus === 'Closed' ? 'View' : 'Edit',
onClick: () => {
Expand Down Expand Up @@ -270,6 +278,7 @@ GoalCard.propTypes = {
recipientId: PropTypes.string.isRequired,
regionId: PropTypes.string.isRequired,
showCloseSuspendGoalModal: PropTypes.func.isRequired,
showReopenGoalModal: PropTypes.func.isRequired,
performGoalStatusUpdate: PropTypes.func.isRequired,
handleGoalCheckboxSelect: PropTypes.func.isRequired,
isChecked: PropTypes.bool.isRequired,
Expand Down
37 changes: 36 additions & 1 deletion frontend/src/components/GoalCards/GoalCards.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import GoalsCardsHeader from './GoalsCardsHeader';
import Container from '../Container';
import GoalCard from './GoalCard';
import CloseSuspendReasonModal from '../CloseSuspendReasonModal';
import { updateGoalStatus } from '../../fetchers/goals';
import { reopenGoal, updateGoalStatus } from '../../fetchers/goals';
import ReopenReasonModal from '../ReopenReasonModal';

function GoalCards({
recipientId,
Expand Down Expand Up @@ -46,6 +47,11 @@ function GoalCards({
const [resetModalValues, setResetModalValues] = useState(false);
const closeSuspendModalRef = useRef();

// Reopen reason modal.
const [reopenGoalId, setReopenGoalId] = useState(null);
const [resetReopenModalValues, setResetReopenModalValues] = useState(false);
const reopenModalRef = useRef();

const showCloseSuspendGoalModal = (status, goalIds, oldGoalStatus) => {
setCloseSuspendGoalIds(goalIds);
setCloseSuspendStatus(status);
Expand All @@ -54,6 +60,27 @@ function GoalCards({
closeSuspendModalRef.current.toggleModal(true);
};

const showReopenGoalModal = (goalId) => {
setReopenGoalId(goalId);
setResetReopenModalValues(!resetReopenModalValues);
reopenModalRef.current.toggleModal(true);
};

const onSubmitReopenGoal = async (goalId, reopenReason, reopenContext) => {
const updatedGoal = await reopenGoal(goalId, reopenReason, reopenContext);

const newGoals = goals.map((g) => (g.id === updatedGoal.id ? {
...g,
goalStatus: 'In Progress',
previousStatus: 'Closed',
isReopenedGoal: true,
} : g));

setGoals(newGoals);

reopenModalRef.current.toggleModal(false);
};

const performGoalStatusUpdate = async (
goalIds,
newGoalStatus,
Expand Down Expand Up @@ -182,6 +209,13 @@ function GoalCards({
resetValues={resetModalValues}
oldGoalStatus={closeSuspendOldStatus}
/>
<ReopenReasonModal
id="reopen-reason-modal"
modalRef={reopenModalRef}
goalId={reopenGoalId}
resetValues={resetReopenModalValues}
onSubmit={onSubmitReopenGoal}
/>
<GoalsCardsHeader
title="TTA goals and objectives"
count={goalsCount || 0}
Expand Down Expand Up @@ -219,6 +253,7 @@ function GoalCards({
recipientId={recipientId}
regionId={regionId}
showCloseSuspendGoalModal={showCloseSuspendGoalModal}
showReopenGoalModal={showReopenGoalModal}
performGoalStatusUpdate={performGoalStatusUpdate}
handleGoalCheckboxSelect={handleGoalCheckboxSelect}
isChecked={selectedGoalCheckBoxes[goal.id] || false}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/GoalForm/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export default function Form({
recipient,
regionId,
goalTemplateId,
isReopenedGoal,
}) {
const { isAppLoading } = useContext(AppLoadingContext);

Expand Down Expand Up @@ -101,7 +102,7 @@ export default function Form({

const objectiveErrors = errors[FORM_FIELD_INDEXES.OBJECTIVES];

const formTitle = goalNumbers && goalNumbers.length ? `Goal ${goalNumbers.join(', ')}` : 'Recipient TTA goal';
const formTitle = goalNumbers && goalNumbers.length ? `Goal ${goalNumbers.join(', ')}${isReopenedGoal ? '-R' : ''}` : 'Recipient TTA goal';

const showAlert = isOnReport && status !== 'Closed';

Expand Down Expand Up @@ -351,6 +352,7 @@ Form.propTypes = {
})).isRequired,
isNew: PropTypes.bool.isRequired,
goalTemplateId: PropTypes.number,
isReopenedGoal: PropTypes.bool.isRequired,
};

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

const [showForm, setShowForm] = useState(true);
Expand All @@ -98,6 +99,7 @@ export default function GoalForm({
const [goalOnApprovedAR, setGoalOnApprovedReport] = useState(goalDefaults.onApprovedAR);
const [goalOnAnyReport, setGoalOnAnyReport] = useState(goalDefaults.onAnyReport);
const [nudgedGoalSelection, setNudgedGoalSelection] = useState({});
const [isReopenedGoal, setIsReopenedGoal] = useState(goalDefaults.isReopenedGoal);

useDeepCompareEffect(() => {
const newPrompts = grantsToMultiValue(selectedGrants, { ...prompts });
Expand Down Expand Up @@ -172,6 +174,7 @@ export default function GoalForm({
setSource(grantsToMultiValue(selectedGoalGrants, goal.source, ''));
setCreatedVia(goal.createdVia || '');
setGoalCollaborators(goal.collaborators || []);
setIsReopenedGoal(goal.isReopenedGoal || false);

// this is a lot of work to avoid two loops through the goal.objectives
// but I'm sure you'll agree its totally worth it
Expand Down Expand Up @@ -1099,6 +1102,7 @@ export default function GoalForm({
createdVia={createdVia}
collaborators={goalCollaborators}
goalTemplateId={goalTemplateId}
isReopenedGoal={isReopenedGoal}
/>
)}

Expand Down
113 changes: 113 additions & 0 deletions frontend/src/components/ReopenReasonModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/* eslint-disable react/jsx-props-no-spreading */
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { REOPEN_REASONS, GOAL_STATUS } from '@ttahub/common';
import {
Form, FormGroup, ErrorMessage, Label, Fieldset, Radio, Textarea,
} from '@trussworks/react-uswds';
import Modal from './Modal';

const ReopenReasonModal = ({
modalRef, goalId, onSubmit, resetValues,
}) => {
const [reopenReason, setReopenReason] = useState('');
const [reopenContext, setReopenContext] = useState('');
const [showValidationError, setShowValidationError] = useState(false);

useEffect(() => {
// Every time we show the modal reset the form.
setReopenReason('');
setReopenContext('');
setShowValidationError(false);
}, [resetValues]);

const reasonChanged = (e) => {
setReopenReason(e.target.value);
setShowValidationError(false);
};

const reasonRadioOptions = Object.values(REOPEN_REASONS[GOAL_STATUS.CLOSED]);

const generateReasonRadioButtons = () => reasonRadioOptions.map((r) => (
<Radio
id={r.trim().replace(' ', '-').toLowerCase()}
key={r}
onChange={reasonChanged}
name="reopenReason"
label={r}
value={r}
className="smart-hub--report-checkbox"
checked={reopenReason === r}
/>
));
const contextChanged = (e) => {
setReopenContext(e.target.value);
};

const validateSubmit = () => {
if (!reopenReason) {
setShowValidationError(true);
} else {
onSubmit(goalId, reopenReason, reopenContext);
}
};

return (
<div className="smart-hub--goal-close-suspend-reason">
<Modal
modalRef={modalRef}
onOk={validateSubmit}
modalId="ReopenReasonModal"
title="Why are you reopening this goal?"
okButtonText="Submit"
okButtonAriaLabel="Reopen goal"
okButtonCss="usa-button--primary"
cancelButtonCss="usa-button--unstyled"
showTitleRequired
forceAction
>
<Form
key={`reopen-reason-form-goal-${goalId}`}
>
<FormGroup error={showValidationError} className="margin-top-0">
<ErrorMessage>
{showValidationError
? 'Please select a reason for reopening this goal.' : null}
</ErrorMessage>
<Fieldset>
{
generateReasonRadioButtons()
}
</Fieldset>
</FormGroup>
<FormGroup>
<Fieldset>
<Label htmlFor="reopen-reason-context">
Additional context
</Label>
<Textarea
id="reopen-reason-context"
name="reopen-reason-context"
type="text"
value={reopenContext}
onChange={contextChanged}
/>
</Fieldset>
</FormGroup>
</Form>
</Modal>
</div>
);
};

ReopenReasonModal.propTypes = {
modalRef: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape(),
]).isRequired,
goalId: PropTypes.number.isRequired,
onSubmit: PropTypes.func.isRequired,
resetValues: PropTypes.bool.isRequired,
};

export default ReopenReasonModal;
Loading