Skip to content

Commit

Permalink
[TM-1466] add donut chart for eco region monitored data (#736)
Browse files Browse the repository at this point in the history
  • Loading branch information
cesarLima1 authored Dec 12, 2024
1 parent ecd3ec4 commit 1bf04ff
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Icon, { IconNames } from "@/components/extensive/Icon/Icon";
import {
CHART_TYPES,
DEFAULT_POLYGONS_DATA,
DEFAULT_POLYGONS_DATA_ECOREGIONS,
DEFAULT_POLYGONS_DATA_STRATEGIES,
DUMMY_DATA_FOR_CHART_SIMPLE_BAR_CHART
} from "@/constants/dashboardConsts";
Expand All @@ -39,12 +40,14 @@ import { TOTAL_HECTARES_UNDER_RESTORATION_TOOLTIP } from "@/pages/dashboard/cons
import { EntityName, OptionValue } from "@/types/common";
import {
isEmptyChartData,
parsePolygonsIndicatorDataForEcoRegion,
parsePolygonsIndicatorDataForLandUse,
parsePolygonsIndicatorDataForStrategies
} from "@/utils/dashboardUtils";
import { downloadFileBlob } from "@/utils/network";

import { useMonitoredData } from "../hooks/useMonitoredData";
import EcoRegionDoughnutChart from "./EcoRegionDoughnutChart";

interface TableData {
polygonName: string;
Expand Down Expand Up @@ -413,6 +416,10 @@ const DataCard = ({
? parsePolygonsIndicatorDataForStrategies(polygonsIndicator)
: DEFAULT_POLYGONS_DATA_STRATEGIES;

const ecoRegionData: any = polygonsIndicator
? parsePolygonsIndicatorDataForEcoRegion(polygonsIndicator)
: DEFAULT_POLYGONS_DATA_ECOREGIONS;

const [topHeaderFirstTable, setTopHeaderFirstTable] = useState("102px");
const [topHeaderSecondTable, setTopHeaderSecondTable] = useState("70px");

Expand Down Expand Up @@ -675,7 +682,7 @@ const DataCard = ({
<img src="/images/monitoring-graph-2.png" alt="" className="w-[73%] object-contain" />
</When>
<When condition={selected.includes("3")}>
<img src="/images/monitoring-graph-3.png" alt="" className="w-[73%] object-contain" />
<EcoRegionDoughnutChart data={ecoRegionData} />
</When>
<When condition={selected.includes("4")}>
<div className="flex w-full flex-col gap-6 lg:ml-[35px]">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { useState } from "react";
import { Cell, Label, Legend, Pie, PieChart, ResponsiveContainer, Sector, Tooltip } from "recharts";

interface ChartDataItem {
name: string;
value: number;
}

export interface EcoRegionData {
chartData: ChartDataItem[];
total: number;
}

interface EcoRegionDoughnutChartProps {
data: EcoRegionData;
}

type LegendPayload = {
value: string;
id?: string;
type?: string;
color?: string;
};

interface CustomLegendProps {
payload?: LegendPayload[];
}

const COLORS = ["#FFD699", "#90EE90", "#2F4F4F", "#BDB76B", "#98FB98"];

const CustomLegend = ({ payload }: CustomLegendProps) => {
if (!payload) return null;
return (
<ul className="flex flex-col gap-2 text-sm">
{payload.map((entry, index) => (
<li key={index} className="flex items-center gap-2">
<span className="inline-block h-3 w-3 rounded-full" style={{ backgroundColor: entry.color }} />
<span>{entry.value}</span>
</li>
))}
</ul>
);
};

const CustomTooltip = ({ active, payload }: any) => {
if (active && payload && payload.length) {
return (
<div className="shadow-lg rounded border bg-white p-2">
<p className="font-medium">{payload[0].name}</p>
<p className="text-gray-600">{`Value: ${payload[0].value}`}</p>
</div>
);
}
return null;
};

const renderActiveShape = (props: any) => {
const { cx, cy, innerRadius, outerRadius, startAngle, endAngle, fill } = props;
return (
<g>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius + 10}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
</g>
);
};

const EcoRegionDoughnutChart: React.FC<EcoRegionDoughnutChartProps> = ({ data }) => {
const { chartData } = data;
const [activeIndex, setActiveIndex] = useState<number | undefined>(undefined);

const onPieEnter = (_: any, index: number) => {
setActiveIndex(index);
};

const onPieLeave = () => {
setActiveIndex(undefined);
};

return (
<div className="relative h-80 w-full">
<ResponsiveContainer width="100%" height="100%">
<PieChart margin={{ right: 0, left: 0, top: 0, bottom: 0 }}>
<Tooltip content={<CustomTooltip />} />
<Pie
data={chartData}
cx={200}
cy="50%"
innerRadius={100}
outerRadius={140}
paddingAngle={0}
dataKey="value"
onMouseEnter={onPieEnter}
onMouseLeave={onPieLeave}
activeIndex={activeIndex}
activeShape={renderActiveShape}
>
<Label position="center" className="text-sm font-bold">
ECO-REGION
</Label>
{chartData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<Legend
content={props => <CustomLegend {...props} />}
layout="vertical"
align="right"
verticalAlign="middle"
wrapperStyle={{
right: 0,
left: 400,
paddingLeft: 0
}}
/>
</PieChart>
</ResponsiveContainer>
</div>
);
};

export default EcoRegionDoughnutChart;
5 changes: 5 additions & 0 deletions src/constants/dashboardConsts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ export const DEFAULT_POLYGONS_DATA_STRATEGIES = [
{ label: "Multiple Strategies", value: 0 }
];

export const DEFAULT_POLYGONS_DATA_ECOREGIONS = {
chartData: [],
total: 0
};

export const DUMMY_DATA_FOR_CHART_MULTI_LINE_CHART = [
{
name: "Total",
Expand Down
30 changes: 30 additions & 0 deletions src/utils/dashboardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ export interface ParsedPolygonsData {
};
}

interface ParsedResult {
chartData: ChartDataItem[];
total: number;
}

export const formatNumberUS = (value: number) =>
value ? (value >= 1000000 ? `${(value / 1000000).toFixed(2)}M` : value.toLocaleString("en-US")) : "";

Expand Down Expand Up @@ -519,3 +524,28 @@ export const parsePolygonsIndicatorDataForStrategies = (polygonsIndicator: Polyg
value: Number(value.toFixed(2))
}));
};

export const parsePolygonsIndicatorDataForEcoRegion = (polygons: PolygonIndicator[]): ParsedResult => {
const result: ParsedResult = {
chartData: [],
total: 0
};

const ecoRegionMap = new Map<string, number>();

polygons.forEach(polygon => {
polygon.data &&
Object.entries(polygon.data).forEach(([name, value]) => {
ecoRegionMap.set(name, (ecoRegionMap.get(name) || 0) + value);
});
});

result.chartData = Array.from(ecoRegionMap, ([name, value]) => ({
name: formatLabel(name),
value: Number(value.toFixed(3))
}));

result.total = Number(result.chartData.reduce((sum, item) => sum + Number(item.value), 0).toFixed(3));

return result;
};

0 comments on commit 1bf04ff

Please sign in to comment.