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
14 changes: 12 additions & 2 deletions frontend/src/api/opsAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,15 @@ export const opsApi = createApi({
}),
invalidatesTags: ["Cans"]
}),
addCanFundingBudgets: builder.mutation({
query: ({ data }) => ({
url: `/can-funding-budgets/`,
method: "POST",
headers: { "Content-Type": "application/json" },
body: data
}),
invalidatesTags: ["Cans"]
}),
getCanFundingSummary: builder.query({
query: ({ ids, fiscalYear, activePeriod, transfer, portfolio, fyBudgets }) => {
const queryParams = [];
Expand Down Expand Up @@ -331,8 +340,8 @@ export const opsApi = createApi({
invalidatesTags: ["ServicesComponents", "Agreements", "BudgetLineItems", "AgreementHistory"]
}),
getChangeRequestsList: builder.query({
query: ({userId}) => ({
url: `/change-requests/${userId ? `?userId=${userId}` : ""}`,
query: ({ userId }) => ({
url: `/change-requests/${userId ? `?userId=${userId}` : ""}`
}),
providesTags: ["ChangeRequests"]
}),
Expand Down Expand Up @@ -413,6 +422,7 @@ export const {
useGetCansQuery,
useGetCanByIdQuery,
useUpdateCanMutation,
useAddCanFundingBudgetsMutation,
useGetCanFundingSummaryQuery,
useGetNotificationsByUserIdQuery,
useGetNotificationsByUserIdAndAgreementIdQuery,
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"
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { NO_DATA } from "../../../constants";

/**
* @typedef {Object} CANFundingReceivedTableProps
* @property {number} totalFunding
* @property {string} totalFunding
* @property {FundingReceived[]} fundingReceived data for table
*/

Expand Down
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 @@ -7,8 +7,8 @@ import Tag from "../../Tag";
/**
* @typedef {Object} BudgetCardProps
* @property {string} title - The title of the card.
* @property {number} totalReceived - The total received.
* @property {number} totalFunding - The total funding.
* @property {string} totalReceived - The total received.
* @property {string} totalFunding - The total funding.
* @property {string} [tagText] - The text for the tag.
* @property {string} [helperText] - The helper text.
*/
Expand Down
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
27 changes: 17 additions & 10 deletions frontend/src/pages/cans/detail/Can.hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import { useGetCanByIdQuery, useGetCanFundingSummaryQuery } from "../../../api/opsAPI";
import { USER_ROLES } from "../../../components/Users/User.constants";
import { getTypesCounts } from "./Can.helpers";
import { NO_DATA } from "../../../constants";
import { getTypesCounts } from "./Can.helpers";

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 @@ -19,10 +20,15 @@ export default function useCan() {
const urlPathParams = useParams();
const canId = parseInt(urlPathParams.id ?? "-1");
/** @type {{data?: CAN | undefined, isLoading: boolean}} */
const { data: can, isLoading } = useGetCanByIdQuery(canId);

const { data: can, isLoading } = useGetCanByIdQuery(canId, {
refetchOnMountOrArgChange: true
});
/** @type {{data?: FundingSummary | undefined, isLoading: boolean}} */
const { data: CANFunding, isLoading: CANFundingLoading } = useGetCanFundingSummaryQuery({
ids: [canId],
fiscalYear: fiscalYear
fiscalYear: fiscalYear,
refetchOnMountOrArgChange: true
});

const budgetLineItemsByFiscalYear = React.useMemo(() => {
Expand Down Expand Up @@ -82,13 +88,14 @@ export default function useCan() {
teamLeaders: can?.portfolio?.team_leaders ?? [],
portfolioName: can?.portfolio?.name,
portfolioId: can?.portfolio_id ?? -1,
totalFunding: CANFunding?.total_funding,
plannedFunding: CANFunding?.planned_funding,
obligatedFunding: CANFunding?.obligated_funding,
inExecutionFunding: CANFunding?.in_execution_funding,
inDraftFunding: CANFunding?.in_draft_funding,
expectedFunding: CANFunding?.expected_funding,
receivedFunding: CANFunding?.received_funding,
totalFunding: CANFunding?.total_funding ?? "0",
fpigeonjr marked this conversation as resolved.
Show resolved Hide resolved
plannedFunding: CANFunding?.planned_funding ?? "0",
obligatedFunding: CANFunding?.obligated_funding ?? "0",
inExecutionFunding: CANFunding?.in_execution_funding ?? "0",
inDraftFunding: CANFunding?.in_draft_funding ?? "0",
expectedFunding: CANFunding?.expected_funding ?? "0",
receivedFunding: CANFunding?.received_funding ?? "0",
carryForwardFunding: CANFunding?.carry_forward_funding ?? "0",
subTitle: can?.nick_name ?? "",
projectTypesCount,
budgetLineTypesCount,
Expand Down
30 changes: 25 additions & 5 deletions frontend/src/pages/cans/detail/Can.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from "react";
import { Route, Routes } from "react-router-dom";
import App from "../../../App";
import CanDetailTabs from "../../../components/CANs/CanDetailTabs/CanDetailTabs";
Expand All @@ -23,6 +24,7 @@ const Can = () => {
budgetLineItemsByFiscalYear,
canNumber,
description,
expectedFunding,
nickname,
fundingDetails,
fundingBudgets,
Expand All @@ -41,9 +43,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 +70,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 +92,8 @@ const Can = () => {
teamLeaders={teamLeaders ?? []}
fiscalYear={fiscalYear}
isBudgetTeamMember={isBudgetTeam}
isEditMode={isEditMode}
toggleEditMode={toggleEditMode}
/>
}
/>
Expand All @@ -105,12 +118,19 @@ const Can = () => {
path="funding"
element={
<CanFunding
canId={canId}
canNumber={canNumber}
expectedFunding={expectedFunding}
funding={fundingDetails}
fundingBudgets={fundingBudgets}
fiscalYear={fiscalYear}
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
Loading
Loading