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 Cards on Spending Tab #3051

Merged
merged 43 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
78d387b
refactor: simplify CAN Detail
fpigeonjr Nov 5, 2024
cc8a2b2
style: update breadcrumb for CANs
fpigeonjr Nov 5, 2024
9b4d97a
refactor: add BLIs for table
fpigeonjr Nov 5, 2024
74aa335
feat: adds CanBudgetLines Table and Row
fpigeonjr Nov 6, 2024
47353e1
chore: wip
fpigeonjr Nov 6, 2024
6b22489
feat: filter CAN BLIs by fiscal year
fpigeonjr Nov 6, 2024
a0833d8
feat: adds Expandable Row
fpigeonjr Nov 7, 2024
b8d22cf
style: use correct icon
fpigeonjr Nov 7, 2024
f7c337e
feat: adds messages to in_review BLI
fpigeonjr Nov 7, 2024
26a0abb
test: adds unit/e2e tests
fpigeonjr Nov 7, 2024
724b150
test: wrong can data
fpigeonjr Nov 7, 2024
0855214
Merge branch 'main' into OPS-2672/can_spending
fpigeonjr Nov 7, 2024
4dcce2d
chore: test
fpigeonjr Nov 7, 2024
346be17
feat: adds notes
fpigeonjr Nov 7, 2024
05ffd72
test: adds comments
fpigeonjr Nov 7, 2024
cfd2ce4
chore: wip
fpigeonjr Nov 8, 2024
f24cf6d
refactor: update api calls
fpigeonjr Nov 8, 2024
9c5b43a
Merge branch 'main' into OPS-2672/cards
fpigeonjr Nov 8, 2024
8cc815a
feat: adds BigBudgetCard
fpigeonjr Nov 8, 2024
97d334c
chore: cleanup
fpigeonjr Nov 8, 2024
ea4986e
refactor: :construction: filters budgetline by fiscal year
fpigeonjr Nov 12, 2024
5ed1028
refactor: adds a variable for TBD
fpigeonjr Nov 12, 2024
aa67ae5
style: label changes
fpigeonjr Nov 12, 2024
e4c5805
test: fixes e2e test
fpigeonjr Nov 12, 2024
ff35889
Merge branch 'main' into OPS-2672/cards
fpigeonjr Nov 12, 2024
0f85ce9
feat: adds DonutGraphWithLegend Card
fpigeonjr Nov 12, 2024
7a4768d
docs: adds JSDocs types
fpigeonjr Nov 12, 2024
f6b41b3
refactor: use RoundedBox
fpigeonjr Nov 12, 2024
278a160
feat: adds percentage of CAN
fpigeonjr Nov 13, 2024
ad6c27d
feat: adds ProjectAgreementBLI card
fpigeonjr Nov 13, 2024
6c1f27e
docs: updates types
fpigeonjr Nov 13, 2024
e7243c9
refactor: adds dynamic data to cards
fpigeonjr Nov 13, 2024
23464d2
Merge branch 'main' into OPS-2672/cards
fpigeonjr Nov 13, 2024
3ec2452
fix: a11y issue
fpigeonjr Nov 13, 2024
f738096
Merge branch 'OPS-2672/cards' of github.com:HHS/OPRE-OPS into OPS-267…
fpigeonjr Nov 13, 2024
1d37996
fix: api call
fpigeonjr Nov 13, 2024
60f0fb2
Merge branch 'main' into OPS-2672/cards
fpigeonjr Nov 13, 2024
6ad828b
test: adds e2e test
fpigeonjr Nov 13, 2024
a2b7bea
Merge branch 'OPS-2672/cards' of github.com:HHS/OPRE-OPS into OPS-267…
fpigeonjr Nov 13, 2024
54b0430
Merge branch 'main' into OPS-2672/cards
fpigeonjr Nov 14, 2024
62a9ab6
refactor: better fallback values
fpigeonjr Nov 14, 2024
be30e0c
style: ux review
fpigeonjr Nov 14, 2024
ed6aaf1
test: update test
fpigeonjr Nov 14, 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
9 changes: 8 additions & 1 deletion frontend/cypress/e2e/canDetail.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,20 @@ describe("CAN detail page", () => {
});
it("shows the CAN Spending page", () => {
cy.visit("/cans/504/spending");
cy.get("#fiscal-year-select").select("2021");
cy.get("#fiscal-year-select").select("2043");
cy.get("h1").should("contain", "G994426"); // heading
cy.get("p").should("contain", "HS - 5 Years"); // sub-heading
// should contain the budget line table
cy.get("table").should("exist");
// table should have more than 1 row
cy.get("tbody").children().should("have.length.greaterThan", 1);
cy.get("#big-budget-summary-card").should("exist");
cy.get("#big-budget-summary-card").should("contain", "-$ 3,000,000.00");
cy.get("#project-agreement-bli-card").should("exist");
cy.get("span").should("contain", "3 Draft");
cy.get("span").should("contain", "1 Executing");
cy.get("span").should("contain", "1 Planned");
cy.get("#donut-graph-with-legend-card").should("exist");
// switch to a different fiscal year
cy.get("#fiscal-year-select").select("2022");
// table should not exist
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/api/opsAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ export const opsApi = createApi({
providesTags: ["Cans"]
}),
getCanFundingSummary: builder.query({
query: (id) => `/can-funding-summary/${id}`,
query: ({ id, fiscalYear }) =>
`/can-funding-summary/${id}${fiscalYear ? `?fiscal_year=${fiscalYear}` : ""}`,
providesTags: ["CanFunding"]
}),
getNotificationsByUserId: builder.query({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const AgreementCANReviewAccordion = ({
action,
isApprovePage = false
}) => {
const { data: portfolios, error, isLoading } = useGetPortfoliosQuery();
const { data: portfolios, error, isLoading } = useGetPortfoliosQuery({});
if (isLoading) {
return <div>Loading...</div>;
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Agreements/AgreementTypes.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SafeUser } from "../Users/UserTypes";
import { Project } from "../Projects/ProjectTypes";
import { ResearchProject } from "../Projects/ProjectTypes";
import { BudgetLine } from "../BudgetLineItems/BudgetLineTypes";

export type Agreement = {
Expand All @@ -16,7 +16,7 @@ export type Agreement = {
procurement_tracker_id?: number;
product_service_code?: ProductServiceCode;
product_service_code_id?: number;
project?: Project;
project?: ResearchProject;
project_id?: number;
project_officer_id?: number;
team_members?: SafeUser[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ import { formatDateNeeded } from "../../../helpers/utils";
import Table from "../../UI/Table";
import { TABLE_HEADERS } from "./CABBudgetLineTable.constants";
import CANBudgetLineTableRow from "./CANBudgetLineTableRow";
import { calculatePercent } from "../../../helpers/utils";
/**
* @typedef {import("../../../components/BudgetLineItems/BudgetLineTypes").BudgetLine} BudgetLine
*/

/**
* @typedef {Object} CANBudgetLineTableProps
* @property {BudgetLine[]} budgetLines
* @property {number} totalFunding
*/

/**
* @component - The CAN Budget Line Table.
* @param {CANBudgetLineTableProps} props
* @returns {JSX.Element} - The component JSX.
*/
const CANBudgetLineTable = ({ budgetLines }) => {
const CANBudgetLineTable = ({ budgetLines, totalFunding }) => {
if (budgetLines.length === 0) {
return <p className="text-center">No budget lines have been added to this CAN.</p>;
}
Expand All @@ -31,9 +33,9 @@ const CANBudgetLineTable = ({ budgetLines }) => {
agreementName="TBD"
obligateDate={formatDateNeeded(budgetLine.date_needed || "")}
fiscalYear={budgetLine.fiscal_year || "TBD"}
amount={budgetLine.amount || 0}
amount={budgetLine.amount ?? 0}
fee={budgetLine.proc_shop_fee_percentage}
percentOfCAN={3}
percentOfCAN={calculatePercent(budgetLine.amount ?? 0, totalFunding)}
status={budgetLine.status}
inReview={budgetLine.in_review}
creatorId={budgetLine.created_by}
Expand Down
17 changes: 9 additions & 8 deletions frontend/src/components/CANs/CANFundingCard/CANFundingCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ import CurrencyWithSmallCents from "../../UI/CurrencyWithSmallCents/CurrencyWith
import RoundedBox from "../../UI/RoundedBox";
import Tag from "../../UI/Tag";
import LineGraph from "../../UI/DataViz/LineGraph";

/**
* A component that displays funding information for a CAN
* @component
* @param {Object} props - The props object.
* @param {Object} props.can - The CAN object.
* @param {number} props.pendingAmount - The pending amount.
* @param {boolean} props.afterApproval - A flag indicating whether the funding is after approval.
* @typedef {Object} CANFundingCardProps
* @property {import("../../../components/CANs/CANTypes").CAN} can - The CAN object.
* @property {number} pendingAmount - The pending amount.
* @property {boolean} afterApproval - A flag indicating whether the funding is after approval.
*/
/**
* @component - displays funding information for a CAN in a card format
* @param {CANFundingCardProps} props - The component props.
* @returns {JSX.Element} - The CANFundingCard component.
*/
const CANFundingCard = ({ can, pendingAmount, afterApproval }) => {
const adjustAmount = afterApproval ? pendingAmount : 0;
const canId = can?.id;
const { data, error, isLoading } = useGetCanFundingSummaryQuery(canId);
const { data, error, isLoading } = useGetCanFundingSummaryQuery({ id: canId });

if (isLoading) {
return <div>Loading...</div>;
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/CANs/CANTable/CANTableRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ const CANTableRow = ({
portfolio,
transfer
}) => {
const { data: fundingSummary, isError, isLoading } = useGetCanFundingSummaryQuery(canId);
const {
data: fundingSummary,
isError,
isLoading
} = useGetCanFundingSummaryQuery({ id: canId, fiscalYear: fiscalYear });
const availableFunds = fundingSummary?.available_funding ?? 0;

if (isLoading)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/DebugCode/DebugCode.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import PropTypes from "prop-types";
* @component
* @param {Object} props - The properties passed to the component.
* @param {string} [props.title="DEBUG CODE"] - The title of the debug section.
* @param {Object | []} props.data - The data to be displayed in the debug section.
* @param {Object | any[]} props.data - The data to be displayed in the debug section.
*
* @returns {JSX.Element | null | boolean } The rendered JSX element, or null if not in development mode.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import PropTypes from "prop-types";
import { useSelector } from "react-redux";
import CurrencyCard from "../../UI/Cards/CurrencyCard";
import RoundedBox from "../../UI/RoundedBox";
import Tag from "../../UI/Tag/Tag";

const ProjectsAndAgreements = ({
numberOfProjects = 0,
numOfResearchProjects = 0,
numOfAdminAndSupportProjects = 0
}) => {
const ProjectsAndAgreements = ({ numOfResearchProjects = 3, numOfAdminAndSupportProjects = 2 }) => {
const fiscalYear = useSelector((state) => state.portfolio.selectedFiscalYear);
const projectHeading = `FY ${fiscalYear.value} Projects`;
const agreementHeading = `FY ${fiscalYear.value} Agreements`;
const plannedAgreements = "3";
const executingAgreements = "2";
const obligatedAgreements = "2";
const numberOfAgreements = "7";
const plannedAgreements = 3;
const executingAgreements = 2;
const obligatedAgreements = 2;
const numberOfProjects = numOfResearchProjects + numOfAdminAndSupportProjects;
const numberOfAgreements = plannedAgreements + executingAgreements + obligatedAgreements;

return (
<CurrencyCard>
<RoundedBox className="padding-y-205 padding-x-4 display-inline-block">
<div className="display-flex flex-justify">
{/* NOTE: left side */}
<article>
Expand Down Expand Up @@ -60,7 +57,7 @@ const ProjectsAndAgreements = ({
</div>
</article>
</div>
</CurrencyCard>
</RoundedBox>
);
};

Expand Down
26 changes: 19 additions & 7 deletions frontend/src/components/Projects/ProjectTypes.d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { SafeUser } from "../Users/UserTypes";

export type Project = {
created_by: number | null;
created_on: Date;
description: string;
export type ResearchProject = {
id: number;
description: string;
methodologies: string[];
origination_date: Date;
populations: string[];
short_title: string;
team_leaders: SafeUser[];
origination_date: Date;
short_title: string;
title: string;
updated_on: Date;
url: string;
created_on: any;
updated_on: any;
created_by: any;
updated_by: any;
created_by_user: any;
updated_by_user: any;
};

export type Project = {
id: number;
project_type: string;
title: string;
short_title: string;
description: string;
url?: string;
};
119 changes: 119 additions & 0 deletions frontend/src/components/UI/Cards/BudgetCard/BigBudgetCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import CurrencyFormat from "react-currency-format";
import CurrencyWithSmallCents from "../../CurrencyWithSmallCents/CurrencyWithSmallCents";
import LineGraph from "../../DataViz/LineGraph";
import RoundedBox from "../../RoundedBox";
import Tag from "../../Tag";

/**
* @typedef {Object} BigBudgetCardProps
* @property {string} title - The title of the card.
* @property {number} totalSpending - The total spending.
* @property {number} totalFunding - The total funding.
*/

/**
* @component BigBudgetCard
* @param {BigBudgetCardProps} props - Properties passed to component
* @returns {JSX.Element} - The BudgetSummaryCard component.
*/
const BigBudgetCard = ({ title, totalSpending, totalFunding }) => {
const overBudget = totalSpending > totalFunding;
const remainingBudget = totalFunding - totalSpending;
const graphData = [
{
id: 1,
value: totalSpending,
color: overBudget ? "var(--feedback-error)" : "var(--data-viz-budget-graph-2)"
},
{
id: 2,
value: remainingBudget,
color: overBudget ? "var(--feedback-error)" : "var(--data-viz-budget-graph-1)"
}
];

return (
<>
<RoundedBox
className={`padding-y-205 padding-x-4 display-inline-block width-full`}
id="big-budget-summary-card"
dataCy={`big-budget-summary-card`}
style={{ minHeight: "10.125rem" }}
>
<h3
className="margin-0 margin-bottom-2 font-12px text-base-dark text-normal"
style={{ whiteSpace: "pre-line", lineHeight: "20px" }}
>
{title}
</h3>

<div className="font-32px margin-0 display-flex flex-justify">
<div className="display-flex flex-align-center">
<CurrencyWithSmallCents
amount={remainingBudget}
dollarsClasses="font-sans-xl text-bold margin-bottom-0"
centsStyles={{ fontSize: "10px" }}
/>

{overBudget ? (
<Tag
tagStyle={"lightTextRedBackground"}
className="margin-left-1"
>
<FontAwesomeIcon
icon={faTriangleExclamation}
title="Over Budget"
/>{" "}
Over Budget
</Tag>
) : (
<Tag
tagStyle={"budgetAvailable"}
className="margin-left-1"
>
Available
</Tag>
)}
</div>

<div className="font-12px margin-top-2 display-flex flex-justify-end">
<div>
Spending {""}
<CurrencyFormat
value={totalSpending ?? 0}
displayType={"text"}
thousandSeparator={true}
prefix={"$"}
renderText={(totalSpending) => <span>{totalSpending}</span>}
/>{" "}
of{" "}
<CurrencyFormat
value={totalFunding ?? 0}
displayType={"text"}
thousandSeparator={true}
prefix={"$"}
renderText={(totalFunding) => <span>{totalFunding}</span>}
/>
</div>
</div>
</div>
<div
id="currency-summary-card"
className="margin-top-2"
>
<LineGraph
data={graphData}
isStriped={true}
overBudget={overBudget}
/>
</div>
</RoundedBox>
<p className="font-12px margin-0 text-base-dark">
*Spending equals the sum of Budget Lines in Planned, Executing and Obligated Status
</p>
</>
);
};
export default BigBudgetCard;
Loading
Loading