Skip to content

Commit

Permalink
feat: ✨adds CAN Spending page BLI Table (#3029)
Browse files Browse the repository at this point in the history
* refactor: simplify CAN Detail

prefer parent to do the hard work

* style: update breadcrumb for CANs

* refactor: add BLIs for table

* feat: adds CanBudgetLines Table and Row

* feat: filter CAN BLIs by fiscal year

* feat: adds  Expandable Row

* feat: adds messages to in_review BLI

* feat: adds notes
  • Loading branch information
fpigeonjr authored Nov 8, 2024
1 parent 53ae536 commit 30c81d0
Show file tree
Hide file tree
Showing 12 changed files with 483 additions and 39 deletions.
17 changes: 3 additions & 14 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,10 @@ repos:
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict

- repo: https://github.com/hadolint/hadolint
rev: v2.10.0
hooks:
- id: hadolint
# We're running black, but doing it via nox session instead - see below
# - repo: https://github.com/psf/black
# rev: 22.6.0
# hooks:
# - id: black
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.10.1
hooks:
Expand All @@ -46,21 +40,16 @@ repos:
- css
- html
pass_filenames: false
- repo: local
hooks:
- id: trufflehog
name: TruffleHog
description: Detect secrets in your data.
# For running trufflehog locally, use the following:
# entry: bash -c 'trufflehog git file://. --since-commit HEAD --only-verified --fail'
# For running trufflehog in docker, use the following entry instead:
entry: bash -c 'docker run --rm -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --since-commit HEAD --only-verified --fail'
entry: bash -c 'if command -v podman >/dev/null 2>&1; then podman run --rm -v "$(pwd):/workdir" -i trufflesecurity/trufflehog:latest git file:///workdir --since-commit HEAD --only-verified --fail; elif command -v docker >/dev/null 2>&1; then docker run --rm -v "$(pwd):/workdir" -i trufflesecurity/trufflehog:latest git file:///workdir --since-commit HEAD --only-verified --fail; else echo "Neither docker nor podman found. Please install one of them." && exit 1; fi'
language: system
stages: ["pre-commit", "pre-push"]
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.18.0
hooks:
- id: commitlint
stages: [ commit-msg ]
additional_dependencies: [ "@commitlint/config-conventional" ]
stages: [commit-msg]
additional_dependencies: ["@commitlint/config-conventional"]
language_version: 22.8.0
17 changes: 16 additions & 1 deletion frontend/cypress/e2e/canDetail.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,27 @@ afterEach(() => {
});

describe("CAN detail page", () => {
it("loads", () => {
it("shows relevant CAN data", () => {
cy.visit("/cans/502/");
cy.get("h1").should("contain", "G99PHS9"); // heading
cy.get("p").should("contain", "SSRD - 5 Years"); // sub-heading
cy.get("span").should("contain", "Nicole Deterding"); // team member
cy.get("span").should("contain", "Director Derrek"); // division director
cy.get("span").should("contain", "Program Support"); // portfolio
});
it("shows the CAN Spending page", () => {
cy.visit("/cans/504/spending");
cy.get("#fiscal-year-select").select("2021");
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);
// switch to a different fiscal year
cy.get("#fiscal-year-select").select("2022");
// table should not exist
cy.get("tbody").should("not.exist");
cy.get("p").should("contain", "No budget lines have been added to this CAN.");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const TABLE_HEADERS = ["BL ID #", "Agreement", "Obligate By", "FY", "Total", "% of CAN", "Status"];
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { formatDateNeeded } from "../../../helpers/utils";
import Table from "../../UI/Table";
import { TABLE_HEADERS } from "./CABBudgetLineTable.constants";
import CANBudgetLineTableRow from "./CANBudgetLineTableRow";
/**
* @typedef {import("../../../components/BudgetLineItems/BudgetLineTypes").BudgetLine} BudgetLine
*/

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

/**
* @component - The CAN Budget Line Table.
* @param {CANBudgetLineTableProps} props
* @returns {JSX.Element} - The component JSX.
*/
const CANBudgetLineTable = ({ budgetLines }) => {
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={3}
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>
);
};

export default CANBudgetLineTable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { render, screen } from "@testing-library/react";
import { Provider } from "react-redux";
import CANBudgetLineTable from "./CANBudgetLineTable";
import store from "../../../store";
import { budgetLine } from "../../../tests/data";

describe("CANBudgetLineTable", () => {
const mockBudgetLines = [
{ ...budgetLine, status: "Approved", amount: 1000 },
{ ...budgetLine, status: "Pending", amount: 2000 }
];

it("renders 'No budget lines have been added to this CAN.' when there are no budget lines", () => {
render(
<Provider store={store}>
<CANBudgetLineTable budgetLines={[]} />
</Provider>
);
expect(screen.getByText("No budget lines have been added to this CAN.")).toBeInTheDocument();
});

it("renders table with budget lines", () => {
render(
<Provider store={store}>
<CANBudgetLineTable budgetLines={mockBudgetLines} />
</Provider>
);
expect(screen.getByText("Approved")).toBeInTheDocument();
expect(screen.getByText("Pending")).toBeInTheDocument();
expect(screen.getByText("$1,000.00")).toBeInTheDocument();
expect(screen.getByText("$2,000.00")).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import { faClock } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import CurrencyFormat from "react-currency-format";
import {
formatDateToMonthDayYear,
totalBudgetLineAmountPlusFees,
totalBudgetLineFeeAmount
} from "../../../helpers/utils";
import useGetUserFullNameFromId from "../../../hooks/user.hooks";
import TableRowExpandable from "../../UI/TableRowExpandable";
import {
changeBgColorIfExpanded,
expandedRowBGColor,
removeBorderBottomIfExpanded
} from "../../UI/TableRowExpandable/TableRowExpandable.helpers";
import { useTableRow } from "../../UI/TableRowExpandable/TableRowExpandable.hooks";
import TableTag from "../../UI/TableTag";
import { useChangeRequestsForTooltip } from "../../../hooks/useChangeRequests.hooks";

/**
* @typedef {import("../../../components/BudgetLineItems/BudgetLineTypes").BudgetLine} BudgetLine
*/

/**
* @typedef {Object} CANBudgetLineTableRowProps
* @property {BudgetLine} budgetLine
* @property {number} blId
* @property {string} agreementName - TODO
* @property {string} obligateDate
* @property {number | string } fiscalYear
* @property {number} amount
* @property {number} fee
* @property {number} percentOfCAN - TODO
* @property {string} status
* @property {boolean} inReview
* @property {number} creatorId
* @property {string} creationDate
* @property {string} procShopCode - TODO
* @property {number} procShopFeePercentage
* @property {string} notes
*/

/**
* @component - The CAN Budget Line Table.
* @param {CANBudgetLineTableRowProps} props
* @returns {JSX.Element} - The component JSX.
*/
const CANBudgetLineTableRow = ({
budgetLine,
blId,
agreementName,
obligateDate,
fiscalYear,
amount,
fee,
percentOfCAN,
status,
inReview,
creatorId,
creationDate,
procShopCode,
procShopFeePercentage,
notes
}) => {
const lockedMessage = useChangeRequestsForTooltip(budgetLine);
const { isExpanded, setIsRowActive, setIsExpanded } = useTableRow();
const borderExpandedStyles = removeBorderBottomIfExpanded(isExpanded);
const bgExpandedStyles = changeBgColorIfExpanded(isExpanded);
const budgetLineCreatorName = useGetUserFullNameFromId(creatorId);
const feeTotal = totalBudgetLineFeeAmount(amount, fee);
const budgetLineTotalPlusFees = totalBudgetLineAmountPlusFees(amount, feeTotal);
const displayCreatedDate = formatDateToMonthDayYear(creationDate);

const TableRowData = (
<>
<th
scope="row"
className={borderExpandedStyles}
style={bgExpandedStyles}
>
{blId}
</th>
<td
className={borderExpandedStyles}
style={bgExpandedStyles}
>
{agreementName}
</td>
<td
className={borderExpandedStyles}
style={bgExpandedStyles}
>
{obligateDate}
</td>
<td
className={borderExpandedStyles}
style={bgExpandedStyles}
>
{fiscalYear}
</td>
<td
className={borderExpandedStyles}
style={bgExpandedStyles}
>
<CurrencyFormat
value={budgetLineTotalPlusFees}
displayType={"text"}
thousandSeparator={true}
prefix={"$"}
decimalScale={2}
fixedDecimalScale={true}
/>
</td>
<td
className={borderExpandedStyles}
style={bgExpandedStyles}
>
{percentOfCAN}%
</td>
<td
className={borderExpandedStyles}
style={bgExpandedStyles}
>
<TableTag
status={status}
inReview={inReview}
lockedMessage={lockedMessage}
/>
</td>
</>
);

const ExpandedData = (
<td
colSpan={9}
className="border-top-none"
style={expandedRowBGColor}
>
<div className="display-flex padding-right-9">
<dl className="font-12px">
<dt className="margin-0 text-base-dark">Created By</dt>
<dd
id={`created-by-name-${blId}`}
className="margin-0"
>
{budgetLineCreatorName}
</dd>
<dt className="margin-0 text-base-dark display-flex flex-align-center margin-top-2">
<FontAwesomeIcon
icon={faClock}
className="height-2 width-2 margin-right-1"
/>
{displayCreatedDate}
</dt>
</dl>
<dl
className="font-12px"
style={{ marginLeft: "9.0625rem" }}
>
<dt className="margin-0 text-base-dark">Notes</dt>
<dd
className="margin-0"
style={{ maxWidth: "25rem" }}
>
{notes}
</dd>
</dl>
<div
className="font-12px"
style={{ marginLeft: "15rem" }}
>
<dl className="margin-bottom-0">
<dt className="margin-0 text-base-dark">Procurement Shop</dt>
<dd
className="margin-0"
style={{ maxWidth: "25rem" }}
>
{`${procShopCode}-Fee Rate: ${procShopFeePercentage * 100}%`}
</dd>
</dl>
<div className="font-12px display-flex margin-top-1">
<dl className="margin-0">
<dt className="margin-0 text-base-dark">SubTotal</dt>
<dd className="margin-0">
<CurrencyFormat
value={amount}
displayType={"text"}
thousandSeparator={true}
prefix={"$"}
decimalScale={2}
fixedDecimalScale={true}
/>
</dd>
</dl>
<dl className=" margin-0 margin-left-2">
<dt className="margin-0 text-base-dark">Fees</dt>
<dd className="margin-0">
<CurrencyFormat
value={feeTotal}
displayType={"text"}
thousandSeparator={true}
prefix={"$"}
decimalScale={2}
fixedDecimalScale={true}
/>
</dd>
</dl>
</div>
</div>
</div>
</td>
);

return (
<TableRowExpandable
tableRowData={TableRowData}
expandedData={ExpandedData}
isExpanded={isExpanded}
setIsExpanded={setIsExpanded}
setIsRowActive={setIsRowActive}
/>
);
};

export default CANBudgetLineTableRow;
Loading

0 comments on commit 30c81d0

Please sign in to comment.