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

feat: adds CAN budget form #3212

Merged
merged 13 commits into from
Dec 19, 2024
57 changes: 22 additions & 35 deletions backend/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1155,45 +1155,32 @@ paths:
available_funding:
type: string
carry_forward_funding:
type: integer
can:
type: object
type: string
cans:
type: array
properties:
can:
type: array
items:
type: object
properties:
appropriation_term:
type: integer
authorizer_id:
type: integer
arrangement_type_id:
type: integer
number:
type: string
purpose:
type: string
managing_portfolio_id:
type: integer
nickname:
type: string
appropriation_date:
type: string
description:
type: string
id:
type: integer
expiration_date:
type: string
managing_project_id:
type: integer
properties:
active_period:
type: integer
description:
type: string
display_name:
type: string
id:
type: integer
nick_name:
type: string
number:
type: string
portfolio_id:
type: integer
projects:
type: array
carry_forward_label:
type: string
example: ""
expiration_date:
type: string
example: ""
expected_funding:
type: string
in_draft_funding:
Expand All @@ -1203,9 +1190,9 @@ paths:
new_funding:
type: string
obligated_funding:
type: integer
type: string
planned_funding:
type: integer
type: string
received_funding:
type: string
total_funding:
Expand Down
42 changes: 42 additions & 0 deletions frontend/src/components/CANs/CANBudgetForm/CANBudgetForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import CurrencyInput from "../../UI/Form/CurrencyInput";

/**
* @typedef {Object} CANBudgetFormProps
* @property {number} fiscalYear
* @property {number} budgetAmount
* @property {() => void} setBudgetAmount
* @property {() => void} handleAddBudget
*/

/**
* @component - The CAN Budget Form component.
* @param {CANBudgetFormProps} props
* @returns {JSX.Element} - The component JSX.
*/
const CANBudgetForm = ({ budgetAmount, fiscalYear, handleAddBudget, setBudgetAmount }) => {
return (
<form
onSubmit={(e) => {
handleAddBudget(e);
setBudgetAmount(0);
}}
>
<CurrencyInput
name="budget-amount"
label={`FY ${fiscalYear} CAN Budget`}
onChange={() => {}}
setEnteredAmount={setBudgetAmount}
value={budgetAmount || ""}
/>
<button
id="save-changes"
className="usa-button usa-button--outline margin-top-4"
disabled={!budgetAmount}
data-cy="save-btn"
>
+ Add FY Budget
</button>
</form>
);
};
export default CANBudgetForm;
1 change: 1 addition & 0 deletions frontend/src/components/CANs/CANBudgetForm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {default} from "./CANBudgetForm"
21 changes: 21 additions & 0 deletions frontend/src/components/CANs/CANTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type BasicCAN = {
nick_name?: string;
number: string;
portfolio_id: number;
projects: Project[];
};

export type URL = {
Expand Down Expand Up @@ -105,3 +106,23 @@ export type FundingReceived = {
updated_by_user?: any;
updated_on?: any;
};

export type FundingSummary = {
available_funding: string;
cans: FundingSummaryCAN[];
carry_forward_funding: string;
expected_funding: string;
in_draft_funding: string;
in_execution_funding: string;
new_funding: string;
obligated_funding: string;
planned_funding: string;
received_funding: string;
total_funding: string;
};

export type FundingSummaryCAN = {
can: BasicCAN;
carry_forward_label: string;
expiration_date: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import CurrencyFormat from "react-currency-format";
* @param {Function} props.onChange - A function to call when the input value changes.
* @param {boolean} [props.pending] - A flag to indicate if the input is pending (optional).
* @param {Array<String>} [props.messages] - An array of error messages to display (optional).
* @param {string} [props.value] - The value of the input field.(optional)
* @param {string | number} [props.value] - The value of the input field.(optional)
* @param {string} [props.className] - Additional CSS classes to apply to the component (optional).
* @param {Function} [props.setEnteredAmount] - A function to call when the input value changes.
* @returns {JSX.Element} - The rendered component.
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/pages/cans/detail/Can.hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { NO_DATA } from "../../../constants";
export default function useCan() {
/**
* @typedef {import("../../../components/CANs/CANTypes").CAN} CAN
* * @typedef {import("../../../components/CANs/CANTypes").FundingSummary} FundingSummary
*/

const activeUser = useSelector((state) => state.auth.activeUser);
Expand All @@ -20,6 +21,7 @@ export default function useCan() {
const canId = parseInt(urlPathParams.id ?? "-1");
/** @type {{data?: CAN | undefined, isLoading: boolean}} */
const { data: can, isLoading } = useGetCanByIdQuery(canId);
/** @type {{data?: FundingSummary | undefined, isLoading: boolean}} */
const { data: CANFunding, isLoading: CANFundingLoading } = useGetCanFundingSummaryQuery({
ids: [canId],
fiscalYear: fiscalYear
Expand Down Expand Up @@ -89,6 +91,7 @@ export default function useCan() {
inDraftFunding: CANFunding?.in_draft_funding,
expectedFunding: CANFunding?.expected_funding,
receivedFunding: CANFunding?.received_funding,
carryForwardFunding: CANFunding?.carry_forward_funding,
subTitle: can?.nick_name ?? "",
projectTypesCount,
budgetLineTypesCount,
Expand Down
26 changes: 21 additions & 5 deletions frontend/src/pages/cans/detail/Can.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import useCan from "./Can.hooks";
import CanDetail from "./CanDetail";
import CanFunding from "./CanFunding";
import CanSpending from "./CanSpending";
import React from "react";
/**
* @typedef {import("../../../components/CANs/CANTypes").CAN} CAN
*/
Expand Down Expand Up @@ -41,9 +42,16 @@ const Can = () => {
budgetLineTypesCount,
agreementTypesCount,
receivedFunding,
isBudgetTeam
isBudgetTeam,
carryForwardFunding
} = useCan();

const [isEditMode, setIsEditMode] = React.useState(false);
fpigeonjr marked this conversation as resolved.
Show resolved Hide resolved

const toggleEditMode = () => {
setIsEditMode(!isEditMode);
};

if (isLoading || CANFundingLoading) {
return <p>Loading CAN...</p>;
}
Expand All @@ -61,10 +69,12 @@ const Can = () => {

<section className="display-flex flex-justify margin-top-3">
<CanDetailTabs canId={canId} />
<CANFiscalYearSelect
fiscalYear={fiscalYear}
setSelectedFiscalYear={setSelectedFiscalYear}
/>
{!isEditMode && (
<CANFiscalYearSelect
fiscalYear={fiscalYear}
setSelectedFiscalYear={setSelectedFiscalYear}
/>
)}
</section>
<Routes>
<Route
Expand All @@ -81,6 +91,8 @@ const Can = () => {
teamLeaders={teamLeaders ?? []}
fiscalYear={fiscalYear}
isBudgetTeamMember={isBudgetTeam}
isEditMode={isEditMode}
toggleEditMode={toggleEditMode}
/>
}
/>
Expand Down Expand Up @@ -111,6 +123,10 @@ const Can = () => {
receivedFunding={receivedFunding}
totalFunding={totalFunding}
fundingReceived={fundingReceivedByFiscalYear}
isBudgetTeamMember={isBudgetTeam}
isEditMode={isEditMode}
toggleEditMode={toggleEditMode}
carryForwardFunding={carryForwardFunding}
/>
}
/>
Expand Down
14 changes: 6 additions & 8 deletions frontend/src/pages/cans/detail/CanDetail.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { faPen } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react";
import { useGetDivisionQuery } from "../../../api/opsAPI";
import CANDetailForm from "../../../components/CANs/CANDetailForm";
import CANDetailView from "../../../components/CANs/CANDetailView";
Expand All @@ -24,6 +23,8 @@ import useGetUserFullNameFromId from "../../../hooks/user.hooks";
* @property {number} divisionId
* @property {number} fiscalYear
* @property {boolean} isBudgetTeamMember
* @property {boolean} isEditMode
* @property {() => void} toggleEditMode
*/

/**
Expand All @@ -41,15 +42,12 @@ const CanDetail = ({
teamLeaders,
divisionId,
fiscalYear,
isBudgetTeamMember
isBudgetTeamMember,
isEditMode,
toggleEditMode
}) => {
const { data: division, isSuccess } = useGetDivisionQuery(divisionId);
const divisionDirectorFullName = useGetUserFullNameFromId(isSuccess ? division.division_director_id : null);
const [isEditMode, setIsEditMode] = React.useState(false);

const toggleEditMode = () => {
setIsEditMode(!isEditMode);
};

const currentFiscalYear = getCurrentFiscalYear();
const showButton = isBudgetTeamMember && fiscalYear === Number(currentFiscalYear);
Expand All @@ -59,7 +57,7 @@ const CanDetail = ({
<article>
<div className="display-flex flex-justify">
<h2>{!isEditMode ? "CAN Details" : "Edit CAN Details"}</h2>
{showButton && (
{showButton && !isEditMode && (
<button
id="edit"
className="hover:text-underline cursor-pointer"
Expand Down
57 changes: 57 additions & 0 deletions frontend/src/pages/cans/detail/CanFunding.hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from "react";
import { getCurrentFiscalYear } from "../../../helpers/utils.js";

export default function useCanFunding(fiscalYear, isBudgetTeamMember, toggleEditMode) {
const currentFiscalYear = getCurrentFiscalYear();
const showButton = isBudgetTeamMember && fiscalYear === Number(currentFiscalYear);
const [budgetAmount, setBudgetAmount] = React.useState(0);
const [submittedAmount, setSubmittedAmount] = React.useState(0);
const [showModal, setShowModal] = React.useState(false);
const [modalProps, setModalProps] = React.useState({
heading: "",
actionButtonText: "",
secondaryButtonText: "",
handleConfirm: () => {}
});

const handleAddBudget = (e) => {
e.preventDefault();
setSubmittedAmount(budgetAmount);
};

const handleCancel = (e) => {
e.preventDefault();
setShowModal(true);
setModalProps({
heading: "Are you sure you want to cancel editing? Your changes will not be saved.",
actionButtonText: "Cancel Edits",
secondaryButtonText: "Continue Editing",
handleConfirm: () => cleanUp()
});
};

const cleanUp = () => {
setBudgetAmount(0);
setSubmittedAmount(0);
setShowModal(false);
toggleEditMode();
setModalProps({
heading: "",
actionButtonText: "",
secondaryButtonText: "",
handleConfirm: () => {}
});
};

return {
budgetAmount,
handleAddBudget,
handleCancel,
modalProps,
setBudgetAmount,
setShowModal,
showButton,
showModal,
submittedAmount
};
}
Loading
Loading