Skip to content

Commit

Permalink
[WIP] fetchs keycosts
Browse files Browse the repository at this point in the history
  • Loading branch information
agnlez committed Jan 16, 2025
1 parent b8be986 commit c7cd22d
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Project } from "@shared/entities/projects.entity";
import { PropsWithChildren } from "react";

import { Column } from "@tanstack/react-table";
import { z } from "zod";

Expand Down Expand Up @@ -33,7 +34,7 @@ export const filtersToQueryParams = (
);
};

export const getColumnSortTitle = (column: Column<Partial<Project>>) => {
export const getColumnSortTitle = <T = unknown,>(column: Column<T>) => {
if (!column.getCanSort()) {
return undefined;
}
Expand All @@ -55,3 +56,11 @@ export const getColumnSortTitle = (column: Column<Partial<Project>>) => {

return "Clear sort";
};

export const HeaderText = ({ children }: PropsWithChildren) => (
<span className="text-xs font-normal">{children}</span>
);

export const CellText = ({ children }: PropsWithChildren) => (
<span className="text-sm font-normal">{children}</span>
);
141 changes: 99 additions & 42 deletions client/src/containers/overview/table/view/key-costs/columns.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,99 @@
// import { createColumnHelper } from "@tanstack/react-table";

// const columnHelper = createColumnHelper<{
// // ! these types should be part of the Project entity eventually, we are adding them here to silent TS for now
// // implementationLabor: number;
// // communityBenefitSharingFund: number;
// // monitoringAndMaintenance: number;
// // communityRepresentationLiaison: number;
// // conservationPlanningAndAdmin: number;
// // carbonStandardFees: number;
// }>();

export const TABLE_COLUMNS = [
// columnHelper.accessor("projectName", {
// enableSorting: true,
// header: () => <span>Project Name</span>,
// }),
// columnHelper.accessor("implementationLabor", {
// enableSorting: true,
// header: () => <span>Implementation labor</span>,
// }),
// columnHelper.accessor("communityBenefitSharingFund", {
// enableSorting: true,
// header: () => <span>Community benefit sharing fund</span>,
// }),
// columnHelper.accessor("monitoringAndMaintenance", {
// enableSorting: true,
// header: () => <span>Monitoring and maintenance</span>,
// }),
// columnHelper.accessor("communityRepresentationLiaison", {
// enableSorting: true,
// header: () => <span>Community representation/liaison</span>,
// }),
// columnHelper.accessor("conservationPlanningAndAdmin", {
// enableSorting: true,
// header: () => <span>Conservation planning and admin</span>,
// }),
// columnHelper.accessor("carbonStandardFees", {
// enableSorting: true,
// header: () => <span>Carbon standard fees</span>,
// }),
];
import { ProjectKeyCosts } from "@shared/dtos/projects/project-key-costs.dto";
import { CellContext, createColumnHelper } from "@tanstack/react-table";
import { z } from "zod";

import { formatCurrency } from "@/lib/format";

import { filtersSchema } from "@/app/(overview)/url-store";

import { HeaderText, CellText } from "@/containers/overview/table/utils";
import { KEY_COSTS_LABELS } from "@/containers/overview/table/view/key-costs/constants";

const columnHelper = createColumnHelper<ProjectKeyCosts>();

const renderHeader = (label: string) => {
return function render() {
return <HeaderText>{label}</HeaderText>;
};
};

const renderCell = (props: CellContext<ProjectKeyCosts, number>) => {
const value = props.getValue();
if (value === null || value === undefined) {
return "-";
}
return <CellText>{formatCurrency(value)}</CellText>;
};

export const columns = (filters: z.infer<typeof filtersSchema>) => {
const isNPV = filters.costRangeSelector === "npv";
const implementationLaborAccessor = isNPV
? "implementationLaborNPV"
: "implementationLabor";

const communityBenefitAccessor = isNPV
? "communityBenefitNPV"
: "communityBenefit";

const monitoringMaintenanceAccessor = isNPV
? "monitoringMaintenanceNPV"
: "monitoringMaintenance";

const communityRepresentationAccessor = isNPV
? "communityRepresentationNPV"
: "communityRepresentation";

const conservationPlanningAccessor = isNPV
? "conservationPlanningNPV"
: "conservationPlanning";

const longTermProjectOperatingAccessor = isNPV
? "longTermProjectOperatingNPV"
: "longTermProjectOperating";

const carbonStandardFeesAccessor = isNPV
? "carbonStandardFeesNPV"
: "carbonStandardFees";

return [
columnHelper.accessor("projectName", {
enableSorting: true,
header: renderHeader("Project Name"),
}),
columnHelper.accessor(implementationLaborAccessor, {
enableSorting: true,
header: renderHeader(KEY_COSTS_LABELS[implementationLaborAccessor]),
cell: renderCell,
}),
columnHelper.accessor(communityBenefitAccessor, {
enableSorting: true,
header: renderHeader(KEY_COSTS_LABELS[communityBenefitAccessor]),
cell: renderCell,
}),
columnHelper.accessor(monitoringMaintenanceAccessor, {
enableSorting: true,
header: renderHeader(KEY_COSTS_LABELS[monitoringMaintenanceAccessor]),
cell: renderCell,
}),
columnHelper.accessor(communityRepresentationAccessor, {
enableSorting: true,
header: renderHeader(KEY_COSTS_LABELS[communityRepresentationAccessor]),
cell: renderCell,
}),
columnHelper.accessor(conservationPlanningAccessor, {
enableSorting: true,
header: renderHeader(KEY_COSTS_LABELS[conservationPlanningAccessor]),
cell: renderCell,
}),
columnHelper.accessor(longTermProjectOperatingAccessor, {
enableSorting: true,
header: renderHeader(KEY_COSTS_LABELS[longTermProjectOperatingAccessor]),
cell: renderCell,
}),
columnHelper.accessor(carbonStandardFeesAccessor, {
enableSorting: true,
header: renderHeader(KEY_COSTS_LABELS[carbonStandardFeesAccessor]),
cell: renderCell,
}),
];
};
21 changes: 21 additions & 0 deletions client/src/containers/overview/table/view/key-costs/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { PROJECT_KEY_COSTS_FIELDS } from "@shared/dtos/projects/project-key-costs.dto";

export const KEY_COSTS_LABELS: Omit<
Record<(typeof PROJECT_KEY_COSTS_FIELDS)[number], string>,
"id" | "projectName"
> = {
implementationLabor: "Implementation Labor",
implementationLaborNPV: "Implementation Labor",
communityBenefit: "Community benefit sharing fund",
communityBenefitNPV: "Community benefit sharing fund",
monitoringMaintenance: "Monitoring and Maintenance",
monitoringMaintenanceNPV: "Monitoring and Maintenance",
communityRepresentation: "Community representation/liaison",
communityRepresentationNPV: "Community representation/liaison",
conservationPlanning: "Conservation planning and admin",
conservationPlanningNPV: "Conservation planning and admin",
longTermProjectOperating: "Long-term project operating",
longTermProjectOperatingNPV: "Long-term project operating",
carbonStandardFees: "Carbon standard fees",
carbonStandardFeesNPV: "Carbon standard fees",
};
84 changes: 62 additions & 22 deletions client/src/containers/overview/table/view/key-costs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"use client";

import { useState } from "react";
import { useMemo, useState } from "react";

import { ChevronDownIcon, ChevronUpIcon } from "@radix-ui/react-icons";
import { projectsQuerySchema } from "@shared/contracts/projects.contract";
import { ProjectKeyCosts } from "@shared/dtos/projects/project-key-costs.dto";
import { keepPreviousData } from "@tanstack/react-query";
import {
flexRender,
Expand All @@ -12,12 +13,15 @@ import {
SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useAtom } from "jotai/index";
import { ChevronsUpDownIcon } from "lucide-react";
import { z } from "zod";

import { client } from "@/lib/query-client";
import { queryKeys } from "@/lib/query-keys";
import { cn } from "@/lib/utils";

import { projectDetailsAtom } from "@/app/(overview)/store";
import {
useProjectOverviewFilters,
useTableView,
Expand All @@ -27,12 +31,13 @@ import { useTablePaginationReset } from "@/hooks/use-table-pagination-reset";

import {
filtersToQueryParams,
getColumnSortTitle,
NO_DATA,
} from "@/containers/overview/table/utils";
import { TABLE_COLUMNS } from "@/containers/overview/table/view/key-costs/columns";
import { columns } from "@/containers/overview/table/view/key-costs/columns";

import {
Table,
ScrollableTable,
TableBody,
TableCell,
TableHead,
Expand All @@ -43,7 +48,11 @@ import TablePagination, {
PAGINATION_SIZE_OPTIONS,
} from "@/components/ui/table-pagination";

type filterFields = z.infer<typeof projectsQuerySchema.shape.fields>;
type sortFields = z.infer<typeof projectsQuerySchema.shape.sort>;

export function KeyCostsTable() {
const [, setProjectDetails] = useAtom(projectDetailsAtom);
const [tableView] = useTableView();
const [filters] = useProjectOverviewFilters();
const [sorting, setSorting] = useState<SortingState>([
Expand All @@ -64,13 +73,27 @@ export function KeyCostsTable() {
pagination,
}).queryKey;

const { data, isSuccess } = client.projects.getProjects.useQuery(
const columnsBasedOnFilters = columns(filters);

const { data, isSuccess } = client.projects.getProjectsKeyCosts.useQuery(
queryKey,
{
query: {
...filtersToQueryParams(filters),
fields: columnsBasedOnFilters.map(
(column) => column.accessorKey,
) as filterFields,
...(sorting.length > 0 && {
sort: sorting.map(
(sort) => `${sort.desc ? "-" : ""}${sort.id}`,
) as sortFields,
}),
costRange: filters.costRange,
abatementPotentialRange: filters.abatementPotentialRange,
costRangeSelector: filters.costRangeSelector,
pageNumber: pagination.pageIndex + 1,
pageSize: pagination.pageSize,
partialProjectName: filters.keyword,
},
},
{
Expand All @@ -80,9 +103,13 @@ export function KeyCostsTable() {
},
);

const visibleProjectIds = useMemo(() => {
return data?.data?.map((item) => item.id!) || [];
}, [data]);

const table = useReactTable({
data: isSuccess ? data.data : NO_DATA,
columns: TABLE_COLUMNS,
data: isSuccess ? (data?.data as ProjectKeyCosts[]) : NO_DATA,
columns: columnsBasedOnFilters,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
state: {
Expand All @@ -95,10 +122,10 @@ export function KeyCostsTable() {

return (
<>
<Table>
<TableHeader className="sticky top-0 bg-white">
<ScrollableTable>
<TableHeader className="sticky top-0">
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
<TableRow key={headerGroup.id} className="divide-background">
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
Expand All @@ -110,15 +137,7 @@ export function KeyCostsTable() {
header.column.getCanSort(),
})}
onClick={header.column.getToggleSortingHandler()}
title={
header.column.getCanSort()
? header.column.getNextSortingOrder() === "asc"
? "Sort ascending"
: header.column.getNextSortingOrder() === "desc"
? "Sort descending"
: "Clear sort"
: undefined
}
title={getColumnSortTitle(header.column)}
>
{flexRender(
header.column.columnDef.header,
Expand All @@ -142,25 +161,46 @@ export function KeyCostsTable() {
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
className="group cursor-pointer transition-colors hover:bg-big-stone-950"
key={row.id}
data-state={row.getIsSelected() && "selected"}
onClick={() => {
setProjectDetails({
isOpen: true,
id: row.original.id!,
visibleProjectIds,
});
}}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
<TableCell
key={cell.id}
className={cn({
"group-hover:underline": cell.column.id === "projectName",
"min-w-[200px] max-w-[200px] truncate":
cell.column.id === "projectName",
"px-4 py-2": cell.column.id !== "scoreCardRating",
})}
title={
typeof cell.getValue() === "string"
? (cell.getValue() as string)
: undefined
}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={TABLE_COLUMNS.length}>
<div className="flex flex-1 justify-center">No results.</div>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</ScrollableTable>
<TablePagination
onChangePagination={setPagination}
pagination={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { formatCurrency, formatNumber } from "@/lib/format";

import { filtersSchema } from "@/app/(overview)/url-store";

import { HeaderText, CellText } from "@/containers/overview/table/utils";
import { TableStateWithMaximums } from "@/containers/overview/table/view/overview";

import SingleStackedBarChart from "@/components/ui/bar-chart/single-stacked-bar-chart";
Expand Down Expand Up @@ -58,13 +59,6 @@ const createSegments = (
];
};

const HeaderText = ({ children }: { children: React.ReactNode }) => (
<span className="text-xs font-normal">{children}</span>
);
const CellText = ({ children }: { children: React.ReactNode }) => (
<span className="text-sm font-normal">{children}</span>
);

export const columns = (filters: z.infer<typeof filtersSchema>) => [
columnHelper.accessor("projectName", {
enableSorting: true,
Expand Down

0 comments on commit c7cd22d

Please sign in to comment.