Skip to content

Commit

Permalink
feat: adds CAN Funding YTD table (#3097)
Browse files Browse the repository at this point in the history
* feat: adds Funding Received TYD table

* feat: component for reverse line graph

* refactor: ♻️ use new components (#3114)

* refactor: use LineBar component

* refactor: use Budget Card
  • Loading branch information
fpigeonjr authored Nov 21, 2024
1 parent 84d1d9e commit 7ca2334
Show file tree
Hide file tree
Showing 19 changed files with 492 additions and 202 deletions.
18 changes: 14 additions & 4 deletions frontend/cypress/e2e/canDetail.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ describe("CAN detail page", () => {
.and("contain", "IDDA")
.and("contain", "09/30/25")
.and("contain", "2021");
cy.get("[data-cy=budget-summary-card]")
cy.get("[data-cy=budget-received-card]")
.should("exist")
.and("contain", "$ 2,000,000.00")
.and("contain", "Available")
.and("contain", "FY 2024 Funding Received YTD")
.and("contain", "Spending $4,000,000.00 of $6,000,000.00");
.and("contain", "$ 6,000,000.00")
.and("contain", "Received")
.and("contain", "Received $6,000,000.00 of $10,000,000.00");
cy.get("[data-cy=can-budget-fy-card]")
.should("exist")
.and("contain", "CAN Budget by FY")
Expand All @@ -97,5 +97,15 @@ describe("CAN detail page", () => {
.and("contain", "FY 2022")
.and("contain", "FY 2021")
.and("contain", "$10,000,000.00");
// table should exist and have one row
cy.get("table").should("exist");
cy.get("tbody").children().should("have.length", 1);
// table should contain 509, 2024, $6,000,000.00, 100%
cy.get("tbody")
.children()
.should("contain", "509")
.and("contain", "2024")
.and("contain", "$6,000,000.00")
.and("contain", "60%");
});
});
4 changes: 2 additions & 2 deletions frontend/cypress/e2e/canList.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ describe("CAN List", () => {

it("should display the summary cards", () => {
cy.get("#fiscal-year-select").select("2023");
cy.get("[data-cy='budget-summary-card']").should("exist");
cy.get("[data-cy='budget-summary-card']").contains("FY 2023 CANs Available Budget *");
cy.get("[data-cy='budget-summary-card-2023']").should("exist");
cy.get("[data-cy='budget-summary-card-2023']").contains("FY 2023 CANs Available Budget *");
cy.get("[data-cy='line-graph-with-legend-card']").should("exist");
cy.get("[data-cy='line-graph-with-legend-card']").contains("FY 2023 CANs Total Budget");
});
Expand Down
32 changes: 16 additions & 16 deletions frontend/cypress/e2e/reviewAgreement.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ afterEach(() => {
});

describe("agreement change accordion", () => {
it("handles interactions", () => {
it("handles interactions on agreement 1", () => {
cy.visit("/agreements/review/1").wait(1000);
cy.get("h2").contains("Select Budget Lines").as("acc-btn");
cy.get(".usa-table").should("exist");
Expand All @@ -32,7 +32,7 @@ describe("agreement change accordion", () => {
.each((checkbox) => {
cy.wrap(checkbox).should("be.checked");
});
cy.get('[data-cy="can-funding-summary-card-504"]').within(() => {
cy.get('[data-cy="budget-summary-card-504"]').within(() => {
cy.contains("$5,000,000");
cy.contains("$ 35,000,000");
cy.contains("$40,000,000");
Expand All @@ -52,7 +52,7 @@ describe("agreement change accordion", () => {
});
});
});
it("handles interactions", () => {
it("handles interactions on agreement 9", () => {
cy.visit("/agreements/review/9").wait(1000);
cy.get("h2").contains("Select Budget Lines").as("acc-btn");
cy.get(".usa-table").should("exist");
Expand All @@ -65,20 +65,20 @@ describe("agreement change accordion", () => {
cy.get('input[id="Change Planned Budget Lines to Executing Status"]').check({ force: true });
cy.get('[data-cy="check-all"]').each(($el) => {
cy.wrap($el).check({ force: true });
cy.wait(1);
cy.wait(1000);
});
cy.get('[type="checkbox"]')
.should("have.length", 3)
.each((checkbox) => {
cy.wrap(checkbox).should("be.checked");
});
cy.get('[data-cy="can-funding-summary-card-502"]').within(() => {
cy.get('[data-cy="budget-summary-card-502"]').within(() => {
cy.contains("$10,403,500");
cy.contains("$ 13,596,500");
cy.contains("$24,000,000");
cy.contains("G99PHS9-5Y");
});
cy.get('[data-cy="can-funding-summary-card-512"]').within(() => {
cy.get('[data-cy="budget-summary-card-512"]').within(() => {
cy.contains("$602,000");
cy.contains("$ 1,678,000");
cy.contains("$2,280,000");
Expand Down Expand Up @@ -209,7 +209,7 @@ describe("agreement review CANS accordion", () => {
cy.visit("/agreements/review/1").wait(1000);
// pre-change
cy.get("h2").contains("Review CANs").should("exist");
cy.get('[data-cy="can-funding-summary-card"]').should("not.exist");
cy.get('[data-cy="budget-funding-summary-card"]').should("not.exist");
// select all BLIs to show CANS cards
cy.get("h2").contains("Choose a Status Change").as("acc-btn").should("exist");
cy.get('input[id="Change Draft Budget Lines to Planned Status"]').should("exist").should("not.be.disabled");
Expand All @@ -226,8 +226,8 @@ describe("agreement review CANS accordion", () => {
.each((checkbox) => {
cy.wrap(checkbox).should("be.checked");
});
cy.get('[data-cy="can-funding-summary-card-504"]').should("exist");
cy.get('[data-cy="can-funding-summary-card-504"]').contains("$40,000,000");
cy.get('[data-cy="budget-summary-card-504"]').should("exist");
cy.get('[data-cy="budget-summary-card-504"]').contains("$40,000,000");
});

it("should handle after approval toggle", () => {
Expand All @@ -251,10 +251,10 @@ describe("agreement review CANS accordion", () => {
.each((checkbox) => {
cy.wrap(checkbox).should("be.checked");
});
cy.get('[data-cy="can-funding-summary-card-504"]').should("exist");
cy.get('[data-cy="can-funding-summary-card-504"]').contains("5,000,000");
cy.get('[data-cy="budget-summary-card-504"]').should("exist");
cy.get('[data-cy="budget-summary-card-504"]').contains("5,000,000");
cy.get('[data-cy="button-toggle-After Approval"]').first().click({ force: true });
cy.get('[data-cy="can-funding-summary-card-504"]').contains("3,000,000");
cy.get('[data-cy="budget-summary-card-504"]').contains("3,000,000");
});

it("should handle over budget CANs", () => {
Expand All @@ -272,10 +272,10 @@ describe("agreement review CANS accordion", () => {
cy.wait(1);
});
cy.get('[type="checkbox"]').should("have.length", 17);
cy.get('[data-cy="can-funding-summary-card-507"]').should("exist");
cy.get('[data-cy="can-funding-summary-card-508"]').should("exist");
cy.get('[data-cy="can-funding-summary-card-507"]').contains("Over Budget");
cy.get('[data-cy="can-funding-summary-card-508"]').contains("Over Budget");
cy.get('[data-cy="budget-summary-card-507"]').should("exist");
cy.get('[data-cy="budget-summary-card-508"]').should("exist");
cy.get('[data-cy="budget-summary-card-507"]').contains("Over Budget");
cy.get('[data-cy="budget-summary-card-508"]').contains("Over Budget");
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe("AgreementCANReview", () => {

const headingCard = screen.getByRole("heading", { name: "G994426-1Y CAN Available Budget" });
const toggle = screen.getByRole("button", { name: "Off (Drafts excluded) After Approval" });
const totalSpendingCardBeforeApproval = screen.getByText("$3,000,000");
const totalSpendingCardBeforeApproval = screen.getByText("$3,000,000.00");
const remainingBudgetCardBeforeApproval = screen.getByText("$ 37,000,000");

expect(headingCard).toBeInTheDocument();
Expand All @@ -82,7 +82,7 @@ describe("AgreementCANReview", () => {
);

const toggleAfterApproval = screen.getByRole("button", { name: "On (Drafts included) After Approval" });
const totalSpendingCardAfterApproval1 = screen.getByText("$5,000,000");
const totalSpendingCardAfterApproval1 = screen.getByText("$5,000,000.00");
const remainingBudgetCardAfterApproval1 = screen.getByText("$ 35,000,000");

expect(toggleAfterApproval).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import React from "react";
import CurrencyFormat from "react-currency-format";
import { getDecimalScale } from "../../../../helpers/currencyFormat.helpers";
import Card from "../../../UI/Cards/Card";
import styles from "./BLIsByFYSummaryCard.styles.module.scss";
import LineBar from "../../../UI/DataViz/LineBar";
import { summaryCard } from "./BLIsFYSummaryCard.helpers";

/**
Expand All @@ -13,7 +10,6 @@ import { summaryCard } from "./BLIsFYSummaryCard.helpers";
* @returns {JSX.Element} - The agreement total budget lines card component JSX.
*/
const BLIsByFYSummaryCard = ({ budgetLineItems = [] }) => {
const id = React.useId();
const { chartData } = summaryCard(budgetLineItems);

return (
Expand All @@ -22,32 +18,14 @@ const BLIsByFYSummaryCard = ({ budgetLineItems = [] }) => {
dataCy="blis-by-fy-card"
>
<div>
{chartData.map((item, index) => (
<div
className="display-flex margin-y-105 font-12px"
key={`blis-fy-${index}-${id}`}
>
<span>FY {item.FY}</span>
<div
className="margin-x-1"
style={{ flex: item.ratio }}
>
<div className={styles.barBox}>
<div
className={styles.rightBar}
style={{ backgroundColor: item.color }}
/>
</div>
</div>
<CurrencyFormat
value={item.total}
displayType="text"
thousandSeparator=","
prefix="$"
decimalScale={getDecimalScale(item.total)}
fixedDecimalScale={true}
/>
</div>
{chartData.map((item) => (
<LineBar
key={item.FY}
color={item.color}
ratio={item.ratio}
total={item.total}
title={`FY ${item.FY}`}
/>
))}
</div>
</Card>
Expand Down
98 changes: 7 additions & 91 deletions frontend/src/components/CANs/CANFundingCard/CANFundingCard.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import CurrencyFormat from "react-currency-format";
import { useGetCanFundingSummaryQuery } from "../../../api/opsAPI";
import { calculatePercent } from "../../../helpers/utils";
import CurrencyWithSmallCents from "../../UI/CurrencyWithSmallCents/CurrencyWithSmallCents";
import RoundedBox from "../../UI/RoundedBox";
import Tag from "../../UI/Tag";
import LineGraph from "../../UI/DataViz/LineGraph";
import BudgetCard from "../../UI/Cards/BudgetCard";
/**
* @typedef {Object} CANFundingCardProps
* @property {import("../../../components/CANs/CANTypes").CAN} can - The CAN object.
Expand Down Expand Up @@ -35,91 +28,14 @@ const CANFundingCard = ({ can, pendingAmount, afterApproval }) => {
const availableFunding = Number(data.available_funding);
const totalAccountedFor = totalFunding - availableFunding; // same as adding planned, obligated, in_execution
const totalSpending = totalAccountedFor + adjustAmount;
const remainingBudget = availableFunding - adjustAmount;
const overBudget = remainingBudget < 0;

const canFundingBarData = [
{
id: 1,
label: "Total Spending",
value: totalSpending,
color: overBudget ? "var(--feedback-error)" : "var(--data-viz-budget-graph-2)",
tagStyle: "darkTextWhiteBackground",
tagStyleActive: overBudget ? "lightTextRedBackground" : "lightTextGreenBackground",
percent: `${calculatePercent(totalSpending, totalFunding)}%`
},
{
id: 2,
label: `Remaining Budget`,
value: remainingBudget,
color: overBudget ? "var(--feedback-error)" : "var(--data-viz-budget-graph-1)",
tagStyle: "darkTextWhiteBackground",
tagStyleActive: overBudget ? "lightTextRedBackground" : "darkTextGreyBackground",
percent: `${calculatePercent(remainingBudget, totalFunding)}%`
}
];

return (
<RoundedBox
dataCy={`can-funding-summary-card-${canId}`}
style={{ height: "14.5rem" }}
>
<h3
className="margin-0 margin-bottom-2 font-12px text-base-dark text-normal"
style={{ whiteSpace: "pre-line", lineHeight: "20px" }}
>
{title} <br /> CAN Available Budget
</h3>

<div className="font-32px margin-0 display-flex flex-justify flex-align-end">
<CurrencyWithSmallCents
amount={remainingBudget}
dollarsClasses="font-sans-xl text-bold margin-bottom-0"
centsStyles={{ fontSize: "10px" }}
/>
{overBudget ? (
<Tag tagStyle={"lightTextRedBackground"}>
<FontAwesomeIcon
icon={faTriangleExclamation}
title="Over Budget"
/>{" "}
Over Budget
</Tag>
) : (
<Tag tagStyle={"budgetAvailable"}>Available</Tag>
)}
</div>
<div
id="currency-summary-card"
className="margin-top-2"
>
<LineGraph
data={canFundingBarData}
isStriped={true}
overBudget={overBudget}
/>
</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>
</RoundedBox>
<BudgetCard
cardId={canId}
title={`${title} \n CAN Available Budget`}
totalSpending={totalSpending}
totalFunding={totalFunding}
/>
);
};
export default CANFundingCard;
Loading

0 comments on commit 7ca2334

Please sign in to comment.