Skip to content

Commit

Permalink
feat: ✨adds pagination to Can Budget Lines (#3078)
Browse files Browse the repository at this point in the history
* feat: adds pagination

* feat: adds CANFundingInfoCard
  • Loading branch information
fpigeonjr authored Nov 15, 2024
1 parent 6775158 commit 4788a31
Show file tree
Hide file tree
Showing 18 changed files with 242 additions and 52 deletions.
30 changes: 30 additions & 0 deletions frontend/cypress/e2e/canDetail.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,34 @@ describe("CAN detail page", () => {
cy.get("tbody").should("not.exist");
cy.get("p").should("contain", "No budget lines have been added to this CAN.");
});
it("pagination on the bli table works as expected", () => {
cy.visit("/cans/504/spending");
cy.get("#fiscal-year-select").select("2043");
cy.wait(1000);
cy.get("ul").should("have.class", "usa-pagination__list");
cy.get("li").should("have.class", "usa-pagination__item").contains("1");
cy.get("button").should("have.class", "usa-current").contains("1");
cy.get("li").should("have.class", "usa-pagination__item").contains("2");
cy.get("li").should("have.class", "usa-pagination__item").contains("Next");
cy.get("tbody").find("tr").should("have.length", 3);
cy.get("li")
.should("have.class", "usa-pagination__item")
.contains("Previous")
.find("svg")
.should("have.attr", "aria-hidden", "true");

// go to the second page
cy.get("li").should("have.class", "usa-pagination__item").contains("2").click();
cy.get("button").should("have.class", "usa-current").contains("2");
cy.get("li").should("have.class", "usa-pagination__item").contains("Previous");
cy.get("li")
.should("have.class", "usa-pagination__item")
.contains("Next")
.find("svg")
.should("have.attr", "aria-hidden", "true");

// go back to the first page
cy.get("li").should("have.class", "usa-pagination__item").contains("1").click();
cy.get("button").should("have.class", "usa-current").contains("1");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,7 @@ const BLIStatusSummaryCard = ({ budgetLines }) => {
};

return (
<RoundedBox
className="display-inline-block"
dataCy="bli-status-summary-card"
>
<RoundedBox dataCy="bli-status-summary-card">
<h3 className="margin-0 margin-bottom-3 font-12px text-base-dark text-normal">Budget Lines By Status</h3>

<div className="display-flex flex-justify">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { formatDateNeeded } from "../../../helpers/utils";
import React from "react";
import { calculatePercent, formatDateNeeded } from "../../../helpers/utils";
import Table from "../../UI/Table";
import { TABLE_HEADERS } from "./CABBudgetLineTable.constants";
import CANBudgetLineTableRow from "./CANBudgetLineTableRow";
import { calculatePercent } from "../../../helpers/utils";
import PaginationNav from "../../UI/PaginationNav";
/**
* @typedef {import("../../../components/BudgetLineItems/BudgetLineTypes").BudgetLine} BudgetLine
*/
Expand All @@ -19,33 +20,49 @@ import { calculatePercent } from "../../../helpers/utils";
* @returns {JSX.Element} - The component JSX.
*/
const CANBudgetLineTable = ({ budgetLines, totalFunding }) => {
// TODO: once in prod, change this to 25
const ITEMS_PER_PAGE = 3;
const [currentPage, setCurrentPage] = React.useState(1);
let visibleBudgetLines = [...budgetLines];
visibleBudgetLines = visibleBudgetLines.slice((currentPage - 1) * ITEMS_PER_PAGE, currentPage * ITEMS_PER_PAGE);

if (budgetLines.length === 0) {
return <p className="text-center">No budget lines have been added to this CAN.</p>;
}

return (
<Table tableHeadings={TABLE_HEADERS}>
{budgetLines.map((budgetLine) => (
<CANBudgetLineTableRow
key={budgetLine.id}
budgetLine={budgetLine}
blId={budgetLine.id}
agreementName="TBD"
obligateDate={formatDateNeeded(budgetLine.date_needed || "")}
fiscalYear={budgetLine.fiscal_year || "TBD"}
amount={budgetLine.amount ?? 0}
fee={budgetLine.proc_shop_fee_percentage}
percentOfCAN={calculatePercent(budgetLine.amount ?? 0, totalFunding)}
status={budgetLine.status}
inReview={budgetLine.in_review}
creatorId={budgetLine.created_by}
creationDate={budgetLine.created_on}
procShopCode="TBD"
procShopFeePercentage={budgetLine.proc_shop_fee_percentage}
notes={budgetLine.comments || "No Notes added"}
<>
<Table tableHeadings={TABLE_HEADERS}>
{visibleBudgetLines.map((budgetLine) => (
<CANBudgetLineTableRow
key={budgetLine.id}
budgetLine={budgetLine}
blId={budgetLine.id}
agreementName="TBD"
obligateDate={formatDateNeeded(budgetLine.date_needed || "")}
fiscalYear={budgetLine.fiscal_year || "TBD"}
amount={budgetLine.amount ?? 0}
fee={budgetLine.proc_shop_fee_percentage}
percentOfCAN={calculatePercent(budgetLine.amount ?? 0, totalFunding)}
status={budgetLine.status}
inReview={budgetLine.in_review}
creatorId={budgetLine.created_by}
creationDate={budgetLine.created_on}
procShopCode="TBD"
procShopFeePercentage={budgetLine.proc_shop_fee_percentage}
notes={budgetLine.comments || "No Notes added"}
/>
))}
</Table>
{budgetLines.length > ITEMS_PER_PAGE && (
<PaginationNav
currentPage={currentPage}
setCurrentPage={setCurrentPage}
items={budgetLines}
itemsPerPage={ITEMS_PER_PAGE}
/>
))}
</Table>
)}
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ const CANFundingCard = ({ can, pendingAmount, afterApproval }) => {

return (
<RoundedBox
className={"display-inline-block"}
dataCy={`can-funding-summary-card-${canId}`}
style={{ height: "14.5rem" }}
>
Expand Down
118 changes: 118 additions & 0 deletions frontend/src/components/CANs/CANFundingInfoCard/CANFundingInfoCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { NO_DATA } from "../../../constants";
import Card from "../../UI/Cards/Card";
import TermTag from "../../UI/Term/TermTag";
/**
* @typedef {import("../../../components/CANs/CANTypes").FundingDetails} FundingDetails
*/

/**
* @typedef {Object} CANFundingInfoCard
* @property {FundingDetails} [funding]
* @property {number} fiscalYear
*/

/**
* @component - The CAN Funding component.
* @param {CANFundingInfoCard} props
* @returns {JSX.Element} - The component JSX.
*/
const CANFundingInfoCard = ({ funding, fiscalYear }) => {
if (!funding) {
return <div>No funding information available for this CAN.</div>;
}

return (
<Card className="width-full">
<h3
className="margin-0 margin-bottom-2 font-12px text-base-dark text-normal"
style={{ whiteSpace: "pre-line", lineHeight: "20px" }}
>
{`FY ${fiscalYear} CAN Funding Information`}
</h3>
<div className="grid-row grid-gap">
<div className="grid-col">
<dl>
<TermTag
term="FY"
description={funding.fiscal_year.toString()}
/>
<TermTag
term="Fund Code"
description={funding.fund_code}
/>
</dl>
</div>
<div className="grid-col">
<dl>
{funding.active_period && (
<TermTag
term="Active Period"
description={
funding.active_period > 1
? `${funding.active_period} Years`
: `${funding.active_period} Year`
}
/>
)}
<TermTag
term="Allowance"
description={funding.allowance ?? NO_DATA}
/>
</dl>
</div>
<div className="grid-col">
<dl>
<TermTag
term="Obligate By"
description={funding.obligate_by?.toString() ?? NO_DATA}
/>
<TermTag
term="Allotment"
description={funding.allotment ?? NO_DATA}
/>
</dl>
</div>
<div className="grid-col">
<dl>
{/* TODO: ask where this comes from */}
<TermTag
term="Funding Received*"
description={NO_DATA}
/>
<TermTag
term="Funding Source"
description={funding.funding_source ?? NO_DATA}
/>
</dl>
</div>
<div className="grid-col">
<dl>
<TermTag
term="Funding Method"
description={funding.method_of_transfer}
/>
<TermTag
term="Partner"
description={funding.funding_partner ?? NO_DATA}
/>
</dl>
</div>
<div className="grid-col">
<dl>
{/* TODO: ask where this comes from */}
<TermTag
term="Funding Type*"
description={NO_DATA}
/>
<TermTag
term="Method of Transfer"
description={funding.method_of_transfer ?? NO_DATA}
/>
</dl>
</div>
</div>
</Card>
);
};

export default CANFundingInfoCard;
1 change: 1 addition & 0 deletions frontend/src/components/CANs/CANFundingInfoCard/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./CANFundingInfoCard";
4 changes: 3 additions & 1 deletion frontend/src/components/CANs/CANTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,18 @@ export type FundingBudget = {
};

export type FundingDetails = {
active_period?: number;
allotment?: string;
allowance?: string;
appropriation?: string;
display_name?: string;
fiscal_year: number;
fund_code: string;
funding_partner?: string;
funding_source?: string;
funding_source?: "OPRE" | "ACF" | "HHS";
id: number;
method_of_transfer?: keyof typeof CAN_TRANSFER;
obligate_by?: number;
sub_allowance?: string;
created_by?: any;
created_by_user?: any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const PortfolioFundingByBudgetStatus = () => {
};

return (
<RoundedBox className="display-inline-block">
<RoundedBox>
<h3 className="margin-0 margin-bottom-3 font-12px text-base-dark text-normal">
FY {fiscalYear.value} Budget Status
</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const BigBudgetCard = ({ title, totalSpending, totalFunding }) => {
return (
<>
<RoundedBox
className="display-inline-block width-full"
className="width-full"
id="big-budget-summary-card"
dataCy={`big-budget-summary-card`}
style={{ minHeight: "10.125rem" }}
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/UI/Cards/BudgetCard/BudgetCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ const BudgetCard = ({ title, totalSpending, totalFunding }) => {

return (
<RoundedBox
className="display-inline-block"
dataCy={`budget-summary-card`}
style={{ height: "14.5rem" }}
>
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/components/UI/Cards/Card/Card.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import RoundedBox from "../../RoundedBox";

/**
* @typedef {Object} CardProps
* @typedef {Object & React.HTMLAttributes<HTMLDivElement>} CardProps
* @property {string} [title] - The title of the card.
* @property {string} [dataCy] - The data-cy attribute to add to the card.
* @property {Object} [style] - The style object to apply to the card.
* @property {Object} [rest] - Additional props to be passed to the card.
* @property {React.ReactNode} children - The children to render.
*/

Expand All @@ -17,7 +16,6 @@ import RoundedBox from "../../RoundedBox";
const Card = ({ title, children, dataCy = "", ...rest }) => {
return (
<RoundedBox
className="display-inline-block"
dataCy={dataCy}
data-testid={dataCy}
{...rest} // this is real trust 🧡
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ const DonutGraphWithLegendCard = ({ data, title, totalFunding }) => {
const id = crypto.randomUUID();

return (
<RoundedBox
className="display-inline-block"
id="donut-graph-with-legend-card"
>
<RoundedBox id="donut-graph-with-legend-card">
<h3 className="margin-0 margin-bottom-3 font-12px text-base-dark text-normal">{title}</h3>
<div className="display-flex flex-justify">
<div className={totalFunding > 0 ? `${styles.widthLegend} font-12px` : "width-card-lg font-12px"}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,7 @@ const ProjectAgreementBLICard = ({ fiscalYear, projects, agreements, budgetLines
};

return (
<RoundedBox
className="display-inline-block"
id="project-agreement-bli-card"
>
<RoundedBox id="project-agreement-bli-card">
<div className="display-flex flex-justify">
<article>
<h3 className="margin-0 margin-bottom-3 font-12px text-base-dark text-normal">{projectHeading}</h3>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/UI/RoundedBox/RoundedBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import cssClasses from "./styles.module.css";
* @param {string} [props.id] - Element ID.
* @param {string} [props.dataCy] - Data attribute for Cypress tests.
* @param {Object} [props.style] - Inline styles.
* @param {Object} [props.rest] - Additional props to be passed
* @property {React.HTMLAttributes<HTMLDivElement>} [rest] - Additional props to be spread onto
* @returns {JSX.Element} Rendered component.
*/
const RoundedBox = ({ children, className, dataCy, ...rest }) => {
const cardContainer = `bg-brand-base-light-variant border-base-light font-family-sans display-flex ${cssClasses.container} ${className ? className : ""}`;
const cardContainer = `bg-brand-base-light-variant border-base-light font-family-sans ${cssClasses.container} ${className ? className : ""}`;

return (
<div
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/UI/RoundedBox/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
border-style: solid;
}

.cardBody {
/* .cardBody {
flex: 3;
}
} */
2 changes: 1 addition & 1 deletion frontend/src/pages/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const Home = () => {
return (
<App>
<div className="display-flex flex-justify-center">
<RoundedBox className="margin-top-4 display-inline-block text-center">
<RoundedBox className="margin-top-4 text-center">
<h1>This is the OPRE OPS system prototype</h1>
<p>⚠️Tread with caution</p>
</RoundedBox>
Expand Down
Loading

0 comments on commit 4788a31

Please sign in to comment.