diff --git a/app/components/CapitalProjectsList/CapitalProjectsPanel.test.tsx b/app/components/CapitalProjectsList/CapitalProjectsPanel.test.tsx
new file mode 100644
index 0000000..b65240d
--- /dev/null
+++ b/app/components/CapitalProjectsList/CapitalProjectsPanel.test.tsx
@@ -0,0 +1,17 @@
+import { render, screen } from "@testing-library/react";
+import { CapitalProjectsPanel } from "./CapitalProjectsPanel";
+
+describe("CapitalProjectsPanel", () => {
+ it("should contain a project list", () => {
+ render(
+
+ <>>
+ ,
+ );
+ expect(screen.getByText(/Mapped Capital Projects/)).toBeVisible();
+ });
+});
diff --git a/app/components/CapitalProjectsList/CapitalProjectsPanel.tsx b/app/components/CapitalProjectsList/CapitalProjectsPanel.tsx
new file mode 100644
index 0000000..17558c2
--- /dev/null
+++ b/app/components/CapitalProjectsList/CapitalProjectsPanel.tsx
@@ -0,0 +1,38 @@
+import { Hide, Show } from "@nycplanning/streetscape";
+import {
+ CapitalProjectsAccordionPanel,
+ CapitalProjectsAccordionPanelProps,
+} from "./CapitalProjectsAccordionPanel";
+import {
+ CapitalProjectsDrawer,
+ CapitalProjectsDrawerProps,
+} from "./CapitalProjectsDrawer";
+
+export interface CapitalProjectsPanelProps
+ extends CapitalProjectsAccordionPanelProps,
+ CapitalProjectsDrawerProps {}
+
+export function CapitalProjectsPanel(props: CapitalProjectsPanelProps) {
+ return (
+ <>
+
+
+ {props.children}
+
+
+
+
+ {props.children}
+
+
+ >
+ );
+}
diff --git a/app/components/CapitalProjectsList/index.ts b/app/components/CapitalProjectsList/index.ts
index fbd3eed..90349b3 100644
--- a/app/components/CapitalProjectsList/index.ts
+++ b/app/components/CapitalProjectsList/index.ts
@@ -1,3 +1,4 @@
export { CapitalProjectsAccordionPanel } from "./CapitalProjectsAccordionPanel";
export { CapitalProjectsList } from "./CapitalProjectsList";
export { CapitalProjectsListItem } from "./CapitalProjectsListItem";
+export { CapitalProjectsPanel } from "./CapitalProjectsPanel";
diff --git a/app/routes/boroughs.$boroughId.community-districts.$communityDistrictId.capital-projects.tsx b/app/routes/boroughs.$boroughId.community-districts.$communityDistrictId.capital-projects.tsx
index a5b749a..2997735 100644
--- a/app/routes/boroughs.$boroughId.community-districts.$communityDistrictId.capital-projects.tsx
+++ b/app/routes/boroughs.$boroughId.community-districts.$communityDistrictId.capital-projects.tsx
@@ -1,3 +1,88 @@
+import { Flex } from "@nycplanning/streetscape";
+import { json, LoaderFunctionArgs } from "@remix-run/node";
+import { useLoaderData } from "@remix-run/react";
+import { CapitalProjectsPanel } from "~/components/CapitalProjectsList";
+import { Pagination } from "~/components/Pagination";
+import {
+ findAgencies,
+ findBoroughs,
+ findCapitalProjectsByBoroughIdCommunityDistrictId,
+} from "~/gen";
+
+export async function loader({ request, params }: LoaderFunctionArgs) {
+ const url = new URL(request.url);
+ const itemsPerPage = 7;
+ const pageParam = url.searchParams.get("page");
+ const page = pageParam === null ? 1 : parseInt(pageParam);
+
+ const { boroughId, communityDistrictId } = params;
+ if (
+ boroughId === undefined ||
+ communityDistrictId === undefined ||
+ isNaN(page)
+ ) {
+ throw json("Bad request", { status: 400 });
+ }
+
+ const offset = (page - 1) * itemsPerPage;
+ const projectsByCommunityDistrictPromise =
+ findCapitalProjectsByBoroughIdCommunityDistrictId(
+ boroughId,
+ communityDistrictId,
+ {
+ limit: itemsPerPage,
+ offset: offset,
+ },
+ {
+ baseURL: `${import.meta.env.VITE_ZONING_API_URL}/api`,
+ },
+ );
+
+ const agenciesPromise = findAgencies({
+ baseURL: `${import.meta.env.VITE_ZONING_API_URL}/api`,
+ });
+ const boroughsPromise = findBoroughs({
+ baseURL: `${import.meta.env.VITE_ZONING_API_URL}/api`,
+ });
+ const [agenciesResponse, boroughsResponse, capitalProjectsResponse] =
+ await Promise.all([
+ agenciesPromise,
+ boroughsPromise,
+ projectsByCommunityDistrictPromise,
+ ]);
+ const boroughAbbr = boroughsResponse.boroughs.find(
+ (borough) => borough.id === boroughId,
+ )?.abbr;
+ return {
+ capitalProjectsResponse,
+ agencies: agenciesResponse.agencies,
+ boroughAbbr,
+ communityDistrictId,
+ };
+}
+
export default function CapitalProjectsByBoroughIdCommunityDistrictId() {
- return <>>;
+ const {
+ capitalProjectsResponse: { total: capitalProjectsTotal, capitalProjects },
+ agencies,
+ boroughAbbr,
+ communityDistrictId,
+ } = useLoaderData();
+
+ return (
+
+
+
+
+
+ );
}
diff --git a/app/routes/city-council-districts.$cityCouncilDistrictId.capital-projects.tsx b/app/routes/city-council-districts.$cityCouncilDistrictId.capital-projects.tsx
index f4ffa3e..2cffe44 100644
--- a/app/routes/city-council-districts.$cityCouncilDistrictId.capital-projects.tsx
+++ b/app/routes/city-council-districts.$cityCouncilDistrictId.capital-projects.tsx
@@ -1,29 +1,23 @@
import { LoaderFunctionArgs, json } from "@remix-run/node";
import { findCapitalProjectsByCityCouncilId, findAgencies } from "../gen";
import { useLoaderData } from "@remix-run/react";
-import { CapitalProjectsAccordionPanel } from "../components/CapitalProjectsList";
-import { Flex, Hide, Show } from "@nycplanning/streetscape";
-import { CapitalProjectsDrawer } from "~/components/CapitalProjectsList/CapitalProjectsDrawer";
+import { CapitalProjectsPanel } from "../components/CapitalProjectsList";
+import { Flex } from "@nycplanning/streetscape";
import { Pagination } from "~/components/Pagination";
export async function loader({ request, params }: LoaderFunctionArgs) {
const url = new URL(request.url);
- const agenciesResponse = await findAgencies({
- baseURL: `${import.meta.env.VITE_ZONING_API_URL}/api`,
- });
-
const itemsPerPage = 7;
const pageParam = url.searchParams.get("page");
const page = pageParam === null ? 1 : parseInt(pageParam);
- const offset = (page - 1) * itemsPerPage;
-
const { cityCouncilDistrictId } = params;
- if (cityCouncilDistrictId === undefined) {
+ if (cityCouncilDistrictId === undefined || isNaN(page)) {
throw json("Bad Request", { status: 400 });
}
+ const offset = (page - 1) * itemsPerPage;
- const projectsByCityCouncilDistrictResponse =
- await findCapitalProjectsByCityCouncilId(
+ const projectsByCityCouncilDistrictPromise =
+ findCapitalProjectsByCityCouncilId(
cityCouncilDistrictId,
{
limit: itemsPerPage,
@@ -34,48 +28,41 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
},
);
+ const agenciesPromise = findAgencies({
+ baseURL: `${import.meta.env.VITE_ZONING_API_URL}/api`,
+ });
+
+ const [agenciesResponse, projectsByCityCouncilDistrictResponse] =
+ await Promise.all([agenciesPromise, projectsByCityCouncilDistrictPromise]);
+
return {
- projects: projectsByCityCouncilDistrictResponse,
+ capitalProjectsResponse: projectsByCityCouncilDistrictResponse,
agencies: agenciesResponse.agencies,
- cityCouncilDistrictId: cityCouncilDistrictId,
+ cityCouncilDistrictId,
};
}
export default function CapitalProjectsByCityCouncilDistrict() {
- const { projects, agencies, cityCouncilDistrictId } =
- useLoaderData();
-
- const pagination = (
-
-
-
- );
+ const {
+ capitalProjectsResponse: { total: capitalProjectsTotal, capitalProjects },
+ agencies,
+ cityCouncilDistrictId,
+ } = useLoaderData();
return (
- <>
-
-
- {pagination}
-
-
-
-
- {pagination}
-
-
- >
+
+
+
+
+
);
}