Skip to content

Commit

Permalink
Project details implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
atrincas authored Dec 23, 2024
1 parent c91fa3e commit 715fa05
Show file tree
Hide file tree
Showing 33 changed files with 806 additions and 714 deletions.
27 changes: 25 additions & 2 deletions client/src/app/(overview)/store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { MapMouseEvent } from "react-map-gl";

import {
COST_TYPE_SELECTOR,
PROJECT_PRICE_TYPE,
PROJECT_SIZE_FILTER,
} from "@shared/entities/projects.entity";
import { atom } from "jotai";
import { z } from "zod";

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

export const projectsUIState = atom<{
filtersOpen: boolean;
Expand All @@ -15,8 +23,23 @@ export const popupAtom = atom<{

export const projectDetailsAtom = atom<{
isOpen: boolean;
projectName: string;
id: string;
visibleProjectIds: string[];
}>({
isOpen: false,
projectName: "",
id: "",
visibleProjectIds: [],
});

export type ProjectDetailsFilters = Pick<
z.infer<typeof filtersSchema>,
"projectSizeFilter" | "priceType" | "costRangeSelector"
>;

const INITIAL_FILTERS_STATE: ProjectDetailsFilters = {
projectSizeFilter: PROJECT_SIZE_FILTER.MEDIUM,
priceType: PROJECT_PRICE_TYPE.MARKET_PRICE,
costRangeSelector: COST_TYPE_SELECTOR.NPV,
};

export const projectDetailsFiltersAtom = atom(INITIAL_FILTERS_STATE);
22 changes: 9 additions & 13 deletions client/src/app/(overview)/url-store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { ReactNode } from "react";

import {
ACTIVITY,
RESTORATION_ACTIVITY_SUBTYPE,
Expand All @@ -25,18 +23,16 @@ import { TABLE_VIEWS } from "@/containers/overview/table/toolbar/table-selector"

const SUB_ACTIVITIES = RESTORATION_ACTIVITY_SUBTYPE;

interface ParameterOption {
label: string;
value: string;
disabled?: boolean;
}

export interface Parameter {
key: keyof Omit<z.infer<typeof filtersSchema>, "keyword">;
export interface Parameter<T = keyof z.infer<typeof filtersSchema>> {
key: T;
label: string;
className: string;
options: ParameterOption[];
tooltipContent?: ReactNode;
tooltipContent?: React.ReactNode;
options: {
label: string;
value: string;
disabled?: boolean;
}[];
}

export const filtersSchema = z.object({
Expand Down Expand Up @@ -65,7 +61,7 @@ export const INITIAL_FILTERS_STATE: z.infer<typeof filtersSchema> = {
abatementPotentialRange: INITIAL_ABATEMENT_POTENTIAL_RANGE,
};

export function useGlobalFilters() {
export function useProjectOverviewFilters() {
const [popup, setPopup] = useAtom(popupAtom);
const [filters, setFilters] = useQueryState(
"filters",
Expand Down
71 changes: 0 additions & 71 deletions client/src/components/ui/bar-chart/index.tsx

This file was deleted.

62 changes: 60 additions & 2 deletions client/src/components/ui/graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const getSize = (value: number, total: number) => {
* 2. Split mode (when leftover is provided): Shows total on left and segments with leftover on right
*/
const Graph: FC<GraphProps> = ({ total, leftover, segments }) => {
if (leftover) {
if (typeof leftover === "number") {
return (
<div className="relative h-full min-h-[150px] w-full max-w-[400px] overflow-hidden rounded-md">
<div className="absolute flex h-full w-full flex-row gap-1 rounded-md">
Expand Down Expand Up @@ -149,4 +149,62 @@ const GraphLegend: FC<GraphLegendProps> = ({ items }) => {
);
};

export { Graph, GraphLegend };
interface GraphWithLegendProps {
/** The total value to be displayed */
total: number;
/** Optional value that, when provided, shows a split view with total on left and segments with leftover on right */
leftover?: number;
/** Array of segments with their corresponding legend items */
items: Array<{
value: number;
label: string;
textColor: string;
bgColor: string;
}>;
}

const GraphWithLegend: FC<GraphWithLegendProps> = ({
total,
leftover,
items,
}) => {
return (
<div className="flex min-h-[160px] justify-between gap-4">
<div className="flex max-w-[148px] flex-1 flex-col justify-between">
<div>
<span className="text-xl font-normal">
{renderCurrency(leftover || total)}
</span>
</div>
<GraphLegend
items={[
...(leftover
? [
{
label: "Total Revenue",
textColor: "text-yellow-500",
bgColor: "bg-yellow-500",
},
]
: []),
...items.map(({ label, textColor, bgColor }) => ({
label,
textColor,
bgColor,
})),
]}
/>
</div>
<Graph
total={total}
segments={items.map(({ value, bgColor }) => ({
value,
colorClass: bgColor,
}))}
leftover={leftover}
/>
</div>
);
};

export { Graph, GraphLegend, GraphWithLegend };
4 changes: 2 additions & 2 deletions client/src/containers/overview/filters/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { cn } from "@/lib/utils";
import { projectsUIState } from "@/app/(overview)/store";
import {
INITIAL_FILTERS_STATE,
useGlobalFilters,
useProjectOverviewFilters,
} from "@/app/(overview)/url-store";

import { Button } from "@/components/ui/button";
Expand All @@ -39,7 +39,7 @@ import {
export const FILTERS_SIDEBAR_WIDTH = 320;

export default function ProjectsFilters() {
const [filters, setFilters] = useGlobalFilters();
const [filters, setFilters] = useProjectOverviewFilters();
const setFiltersOpen = useSetAtom(projectsUIState);
const [costValuesState, setCostValuesState] = useState([
filters.costRange[0] || INITIAL_COST_RANGE[filters.costRangeSelector][0],
Expand Down
4 changes: 2 additions & 2 deletions client/src/containers/overview/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Settings2Icon } from "lucide-react";
import { cn } from "@/lib/utils";

import { projectsUIState } from "@/app/(overview)/store";
import { useGlobalFilters } from "@/app/(overview)/url-store";
import { useProjectOverviewFilters } from "@/app/(overview)/url-store";
import { INITIAL_FILTERS_STATE } from "@/app/(overview)/url-store";

import ParametersProjects from "@/containers/overview/header/parameters";
Expand All @@ -17,7 +17,7 @@ import { SidebarTrigger } from "@/components/ui/sidebar";

export default function ProjectsHeader() {
const setFiltersOpen = useSetAtom(projectsUIState);
const [filters] = useGlobalFilters();
const [filters] = useProjectOverviewFilters();

/* eslint-disable @typescript-eslint/no-unused-vars */
const {
Expand Down
16 changes: 10 additions & 6 deletions client/src/containers/overview/header/parameters/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import {
COST_TYPE_SELECTOR,
PROJECT_PRICE_TYPE,
} from "@shared/entities/projects.entity";
import { z } from "zod";

import { FILTER_KEYS } from "@/app/(overview)/constants";
import { Parameter, useGlobalFilters } from "@/app/(overview)/url-store";
import { filtersSchema } from "@/app/(overview)/url-store";
import {
Parameter,
useProjectOverviewFilters,
} from "@/app/(overview)/url-store";

import { FILTERS } from "@/constants/tooltip";

Expand Down Expand Up @@ -80,10 +81,11 @@ export const PROJECT_PARAMETERS: Parameter[] = [
] as const;

export default function ParametersProjects() {
const [filters, setFilters] = useGlobalFilters();
const [filters, setFilters] = useProjectOverviewFilters();

const handleParameters = async (
v: string,
parameter: keyof Omit<z.infer<typeof filtersSchema>, "keyword">,
parameter: keyof typeof filters,
) => {
await setFilters((prev) => ({
...prev,
Expand All @@ -104,7 +106,9 @@ export default function ParametersProjects() {
</InfoButton>
<Select
name={parameter.label}
defaultValue={String(filters[parameter.key])}
defaultValue={String(
filters[parameter.key as keyof typeof filters],
)}
onValueChange={(v) => {
handleParameters(v, parameter.key);
}}
Expand Down
4 changes: 2 additions & 2 deletions client/src/containers/overview/map/layers/projects/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { FillLayerSpecification } from "mapbox-gl";
import { client } from "@/lib/query-client";
import { geometriesKeys } from "@/lib/query-keys";

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

import { generateColorRamp } from "@/containers/overview/map/layers/projects/utils";

export const LAYER_ID = "cost-abatement-layer";

export default function ProjectsLayer() {
const [filters] = useGlobalFilters();
const [filters] = useProjectOverviewFilters();

const queryKey = geometriesKeys.all(filters).queryKey;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { FC } from "react";

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

import { PROJECT_DETAILS } from "@/constants/tooltip";

import { Label } from "@/components/ui/label";

interface AbatementPotentialProps {
value?: number;
}

const AbatementPotential: FC<AbatementPotentialProps> = ({ value }) => {
return (
<>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Label
className="text-md font-medium"
tooltip={{
title: "Abatement potential",
content: PROJECT_DETAILS.ABATEMENT_POTENTIAL,
}}
>
<h3 className="text-base font-semibold">Abatement potential</h3>
</Label>
</div>
<span className="space-x-1 text-xl font-normal">
<span
className={"inline-block align-top text-xs text-muted-foreground"}
>
tCO2e/yr&nbsp;
</span>
<span>{formatNumber(value || 0)}</span>
</span>
</div>
<div className="mt-4 space-y-2 text-sm text-muted-foreground">
<p>Estimation of total CO2 expected during the project.</p>
</div>
</>
);
};

export default AbatementPotential;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Plus } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";

const CompareButton = () => {
return (
<div className="flex items-center gap-2">
<Label className="text-xs text-big-stone-200">
Compare with a different project
</Label>
<Button variant="outline" className="h-8 w-8 p-0 hover:bg-transparent">
<Plus className="h-4 w-4" />
</Button>
</div>
);
};

export default CompareButton;
Loading

0 comments on commit 715fa05

Please sign in to comment.