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

blocked: starts Resolve Agreement page #2741

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import PropTypes from "prop-types";
import React from "react";
import { useNavigate } from "react-router-dom";
import classnames from "vest/classnames";

import {
useAddAgreementMutation,
useDeleteAgreementMutation,
useGetProductServiceCodesQuery,
useUpdateAgreementMutation
} from "../../../api/opsAPI";
import { formatTeamMember } from "../../../api/postAgreements";
import { cleanAgreementForApi } from "../../../helpers/agreement.helpers";
import useAlert from "../../../hooks/use-alert.hooks";
import useHasStateChanged from "../../../hooks/useHasStateChanged.hooks";
import ContractTypeSelect from "../../ServicesComponents/ContractTypeSelect";
Expand Down Expand Up @@ -176,12 +176,6 @@ export const AgreementEditForm = ({
});
};

const cleanAgreementForApi = (data) => {
// eslint-disable-next-line no-unused-vars
const { id, budget_line_items, services_components, created_by, created_on, updated_on, ...cleanData } = data;
return { id: id, cleanData: cleanData };
};

const saveAgreement = async () => {
const data = {
...agreement,
Expand Down Expand Up @@ -328,8 +322,6 @@ export const AgreementEditForm = ({
handleConfirm={modalProps.handleConfirm}
/>
)}
<h2 className="font-sans-lg margin-top-3 margin-bottom-0">Agreement Type</h2>
<p className="margin-top-1">Select the agreement type to get started.</p>
<AgreementTypeSelect
messages={res.getErrors("agreement_type")}
className={cn("agreement_type")}
Expand All @@ -340,12 +332,6 @@ export const AgreementEditForm = ({
runValidate(name, value);
}}
/>
<h2 className="font-sans-lg margin-top-3">Agreement Details</h2>
<p className="margin-top-1">
Tell us a little more about this agreement. Make sure you complete the required information in order to
proceed. For everything else you can skip the parts you do not know or come back to edit the information
later.
</p>
<Input
name="name"
label="Agreement Title"
Expand Down Expand Up @@ -417,7 +403,6 @@ export const AgreementEditForm = ({
onChangeSelectedProcurementShop={handleOnChangeSelectedProcurementShop}
/>
</div>

<div className="display-flex margin-top-3">
<AgreementReasonSelect
name="agreement_reason"
Expand Down Expand Up @@ -452,7 +437,6 @@ export const AgreementEditForm = ({
/>
</fieldset>
</div>

<div className="display-flex margin-top-3">
<ProjectOfficerComboBox
selectedProjectOfficer={selectedProjectOfficer}
Expand All @@ -476,7 +460,6 @@ export const AgreementEditForm = ({
overrideStyles={{ width: "14.375rem" }}
/>
</div>

<h3 className="font-sans-sm text-semibold">Team Members Added</h3>
<TeamMemberList
selectedTeamMembers={selectedTeamMembers}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const suite = create((data = {}, fieldName) => {
});
test("contract-type", "This is required information", () => {
enforce(data.contract_type).notEquals("-Select an option-");
enforce(data.contract_type).isNotEmpty();
});
test("team-members", "This is required information", () => {
enforce(data.team_members).lengthNotEquals(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
BLI_STATUS,
BLILabel,
budgetLinesTotal,
cleanBudgetLineItemForApi,
getNonDRAFTBudgetLines,
groupByServicesComponent
} from "../../../helpers/budgetLines.helpers";
Expand Down Expand Up @@ -550,30 +551,6 @@ const useCreateBLIsAndSCs = (
});
};

const cleanBudgetLineItemForApi = (data) => {
const cleanData = { ...data };
if (data.services_component_id === 0) {
cleanData.services_component_id = null;
}
if (cleanData.date_needed === "--") {
cleanData.date_needed = null;
}
const budgetLineId = cleanData.id;
delete cleanData.created_by;
delete cleanData.created_on;
delete cleanData.updated_on;
delete cleanData.can;
delete cleanData.id;
delete cleanData.in_review;
delete cleanData.canDisplayName;
delete cleanData.versions;
delete cleanData.clin;
delete cleanData.agreement;
delete cleanData.financialSnapshotChanged;

return { id: budgetLineId, data: cleanData };
};

/**
* Set the budget line for editing by its ID
* @param {string} budgetLineId - The ID of the budget line to edit
Expand Down Expand Up @@ -720,8 +697,8 @@ const useCreateBLIsAndSCs = (
handleDuplicateBudgetLine,
isEditing,
budgetLineBeingEdited,
budgetLinePageErrorsExist,
pageErrors,
budgetLinePageErrorsExist,
showModal,
setShowModal,
modalProps,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import PropTypes from "prop-types";
import React from "react";
import { convertCodeForDisplay } from "../../../helpers/utils";
import EditModeTitle from "../../../pages/agreements/EditModeTitle";
import AgreementBudgetLinesHeader from "../../Agreements/AgreementBudgetLinesHeader";
import AgreementTotalCard from "../../Agreements/AgreementDetailsCards/AgreementTotalCard";
Expand Down Expand Up @@ -61,7 +60,6 @@ export const CreateBLIsAndSCs = ({
setIncludeDrafts
}) => {
const {
budgetLinePageErrorsExist,
handleDeleteBudgetLine,
handleDuplicateBudgetLine,
handleEditBLI,
Expand Down Expand Up @@ -210,7 +208,7 @@ export const CreateBLIsAndSCs = ({
/>
)}

{budgetLinePageErrorsExist && (
{pageErrors && (
<ul
className="usa-list--unstyled font-12px text-error"
data-cy="error-list"
Expand All @@ -221,7 +219,6 @@ export const CreateBLIsAndSCs = ({
className="border-left-2px padding-left-1"
data-cy="error-item"
>
<strong>{convertCodeForDisplay("validation", key)}: </strong>
{
<span>
{value.map((message, index) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const suite = create((data) => {
only(data);

// test to ensure at least one budget line item exists
test("data", "Must have at least one budget line item", () => {
enforce(data.budgetLines).longerThan(0);
test("data", "Must have at least one budget line", () => {
enforce(data.budgetLines.length).greaterThan(0);
});
// test budget_line_items array
each(data.budgetLines, (item) => {
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/helpers/agreement.helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,9 @@ export const getProcurementShopSubTotal = (agreement, budgetLines = []) => {

return calculateTotal(agreement.budget_line_items, fee);
};

export const cleanAgreementForApi = (data) => {
// eslint-disable-next-line no-unused-vars
const { id, budget_line_items, services_components, created_by, created_on, updated_on, ...cleanData } = data;
return { id: id, cleanData: cleanData };
};
24 changes: 24 additions & 0 deletions frontend/src/helpers/budgetLines.helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,27 @@ export const isBudgetLineEditableByStatus = (budgetLine) => {

return (isBudgetLineDraft || isBudgetLinePlanned) && !isBudgetLineInReview;
};

export const cleanBudgetLineItemForApi = (data) => {
const cleanData = { ...data };
if (data.services_component_id === 0) {
cleanData.services_component_id = null;
}
if (cleanData.date_needed === "--") {
cleanData.date_needed = null;
}
const budgetLineId = cleanData.id;
delete cleanData.created_by;
delete cleanData.created_on;
delete cleanData.updated_on;
delete cleanData.can;
delete cleanData.id;
delete cleanData.in_review;
delete cleanData.canDisplayName;
delete cleanData.versions;
delete cleanData.clin;
delete cleanData.agreement;
delete cleanData.financialSnapshotChanged;

return { id: budgetLineId, data: cleanData };
};
52 changes: 51 additions & 1 deletion frontend/src/hooks/agreement.hooks.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useSelector } from "react-redux";
import { useGetAgreementByIdQuery } from "../api/opsAPI";
import { useAddAgreementMutation, useGetAgreementByIdQuery, useUpdateAgreementMutation } from "../api/opsAPI";
import { BLI_STATUS } from "../helpers/budgetLines.helpers";
import useAlert from "./use-alert.hooks";
import { cleanAgreementForApi } from "../helpers/agreement.helpers";
import { formatTeamMember } from "../api/postAgreements";
import React from "react";

/**
* Custom hook that returns whether the user is allowed to edit the agreement.
Expand Down Expand Up @@ -46,3 +50,49 @@ export const useIsAgreementEditable = (agreementId) => {

return isAgreementEditable;
};

export const useSaveAgreement = (agreement, selectedTeamMembers, setAgreementId) => {
const [addAgreement] = useAddAgreementMutation();
const [updateAgreement] = useUpdateAgreementMutation();
const { setAlert } = useAlert();

const saveAgreement = React.useCallback(async () => {
const data = {
...agreement,
team_members: selectedTeamMembers.map((team_member) => formatTeamMember(team_member))
};
const { id, cleanData } = cleanAgreementForApi(data);

try {
if (id) {
const fulfilled = await updateAgreement({ id, data: cleanData }).unwrap();
console.log(`UPDATE: agreement updated: ${JSON.stringify(fulfilled, null, 2)}`);
setAlert({
type: "success",
heading: "Agreement Edited",
message: `The agreement ${agreement.name} has been successfully updated.`
});
} else {
const payload = await addAgreement(cleanData).unwrap();
const newAgreementId = payload.id;
setAgreementId(newAgreementId);
console.log(`CREATE: agreement success: ${JSON.stringify(payload, null, 2)}`);
setAlert({
type: "success",
heading: "Agreement Draft Saved",
message: `The agreement ${agreement.name} has been successfully created.`
});
}
} catch (error) {
console.error(`Error saving agreement: ${JSON.stringify(error, null, 2)}`);
setAlert({
type: "error",
heading: "Error",
message: "An error occurred while saving the agreement.",
redirectUrl: "/error"
});
}
}, [agreement, selectedTeamMembers, addAgreement, updateAgreement, setAlert, setAgreementId]);

return { saveAgreement };
};
41 changes: 41 additions & 0 deletions frontend/src/hooks/budget-line.hooks.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { useSelector } from "react-redux";
import { useAddBudgetLineItemMutation, useUpdateBudgetLineItemMutation } from "../api/opsAPI";
import useAlert from "./use-alert.hooks";
import { cleanBudgetLineItemForApi } from "../helpers/budgetLines.helpers";

/**
* @typedef {Object} BudgetLine
Expand All @@ -21,3 +24,41 @@ export const useIsBudgetLineCreator = (/** @type {BudgetLine} */ budgetLine) =>

return isUserBudgetLineCreator;
};

const useSaveBudgetLines = (tempBudgetLines) => {
const [addBudgetLineItem] = useAddBudgetLineItemMutation();
const [updateBudgetLineItem] = useUpdateBudgetLineItemMutation();
const { setAlert } = useAlert();

const saveBudgetLines = async () => {
try {
const promises = tempBudgetLines.map((budgetLine) => {
const { id, data } = cleanBudgetLineItemForApi(budgetLine);
if (id) {
return updateBudgetLineItem({ id, data }).unwrap();
} else {
return addBudgetLineItem(data).unwrap();
}
});

await Promise.all(promises);
setAlert({
type: "success",
heading: "Budget Lines Saved",
message: "All budget lines have been successfully saved."
});
} catch (error) {
console.error(`SAVE: budget lines failed: ${JSON.stringify(error, null, 2)}`);
setAlert({
type: "error",
heading: "Error",
message: "An error occurred while saving the budget lines.",
redirectUrl: "/error"
});
}
};

return { saveBudgetLines };
};

export default useSaveBudgetLines;
15 changes: 15 additions & 0 deletions frontend/src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import PortfolioDetail from "./pages/portfolios/detail/PortfolioDetail";
import PortfolioList from "./pages/portfolios/list/PortfolioList";
import CreateProject from "./pages/projects/CreateProject";
import ResearchProjectDetail from "./pages/researchProjects/detail/ResearchProjectDetail";
import ResolveAgreement from "./pages/agreements/resolve/ResolveAgreement";
import UserAdmin from "./pages/users/admin/UserAdmin.jsx";
import UserDetail from "./pages/users/detail/UserDetail";
import EditUser from "./pages/users/edit/EditUser";
Expand Down Expand Up @@ -231,6 +232,20 @@ const router = createBrowserRouter(
)
}}
/>
<Route
path="/agreements/resolve/:id/*"
element={<ResolveAgreement />}
handle={{
crumb: () => (
<Link
to="/agreements"
className="text-primary"
>
Agreements
</Link>
)
}}
/>
<Route
path="/cans"
element={<CanList />}
Expand Down
Loading
Loading