Skip to content

Commit

Permalink
[TM-1605] site landing page progress goals (#858)
Browse files Browse the repository at this point in the history
* [TM-1605] add component to progress and goals pie chart

* [TM-1605] add mapping to data charts

* [TM-1605] add params to chartData component

* [TM-1605] add external component to progress and goals site

* [TM-1605] update name chartData

* [TM-1605] update styles to tooltip text

* [TM-1605] add total trees restored const

* [TM-1605] add GoalsAndProgressSiteTab component to overview tab

* [TM-1605] update snapshots

* [TM-1605] update mapping param

* [TM-1605] update ProgressGoalsDoughnutChart component

* [TM-1605] change ChartData => ProgressDataCard

* [TM-1605] change terrafund => framework
  • Loading branch information
LimberHope authored Jan 30, 2025
1 parent b3803ec commit ee9362e
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 130 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from "react";
import { Cell, Label, Pie, PieChart, ResponsiveContainer } from "recharts";

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

export interface ProgressGoalsData {
chartData: ChartDataItem[];
}

interface ProgressGoalsDoughnutChartProps {
data?: ProgressGoalsData;
}
const percentage = (current: number, total: number) => {
const percentValue = Math.min((current / total) * 100, 100);
return percentValue.toFixed(0);
};

const ProgressGoalsDoughnutChart: React.FC<ProgressGoalsDoughnutChartProps> = ({ data }) => {
const { chartData } = data as any;

const currentValue = chartData[0]?.value || 0;
const totalValue = chartData[1]?.value || 0;

const remainingValue = Math.max(totalValue - currentValue, 0);

const transformedData =
currentValue > totalValue
? [{ value: 1, isProgress: true }]
: [
{ value: currentValue, isProgress: true },
{ value: remainingValue, isProgress: false }
];
const COLORS = ["#27A9E0", "#DFF2FB"];

return (
<div className="relative flex h-[180px] w-full flex-col items-center justify-center pt-0">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={transformedData}
cx="50%"
cy="50%"
innerRadius={45}
outerRadius={75}
paddingAngle={0}
dataKey="value"
startAngle={90}
endAngle={-270}
>
{totalValue > 0 && (
<Label
position="center"
content={(props: any) => {
const { viewBox } = props;
const { cx, cy } = viewBox;
return (
<text
x={cx}
y={cy}
textAnchor="middle"
dominantBaseline="middle"
className="text-20-semibold !font-semibold !text-darkCustom"
>
<tspan x={cx} dy="-4" className="text-16 !font-bold !text-blueCustom-700">
{currentValue > totalValue ? "100+" : percentage(currentValue, totalValue)}%
</tspan>
<tspan x={cx} dy="16" className="text-12-light !text-darkCustom">
complete
</tspan>
</text>
);
}}
/>
)}
{transformedData.map((entry: any, index: any) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
className={currentValue > totalValue ? "opacity-80" : ""}
/>
))}
</Pie>
</PieChart>
</ResponsiveContainer>
</div>
);
};

export default ProgressGoalsDoughnutChart;
25 changes: 12 additions & 13 deletions src/components/elements/Cards/GoalProgressCard/GoalProgressCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import classNames from "classnames";
import { DetailedHTMLProps, FC, HTMLAttributes } from "react";
import { Else, If, Then, When } from "react-if";
import { When } from "react-if";

import Text from "@/components/elements/Text/Text";
import { withFrameworkShow } from "@/context/framework.provider";
Expand All @@ -22,6 +22,9 @@ export interface GoalProgressCardProps extends DetailedHTMLProps<HTMLAttributes<
labelVariant?: TextVariants;
classNameCard?: string;
classNameLabelValue?: string;
chart?: JSX.Element;
hectares?: boolean;
graph?: boolean;
}

const GoalProgressCard: FC<GoalProgressCardProps> = ({
Expand All @@ -38,6 +41,9 @@ const GoalProgressCard: FC<GoalProgressCardProps> = ({
labelVariant,
classNameCard,
classNameLabelValue,
chart,
hectares = false,
graph = true,
...rest
}) => {
const value = _val ?? 0;
Expand All @@ -53,20 +59,13 @@ const GoalProgressCard: FC<GoalProgressCardProps> = ({
<Text variant={labelVariant ?? "text-16-light"} className={classNames("mb-1 w-full", classNameLabel)}>
{label}
</Text>
<When condition={!!totalValue}>
<If condition={totalValue === "no data"}>
<Then>
<img src="/images/graphic-6.png" alt="arrow-right" className="size-32 lg:size-40 mb-2" />
</Then>
<Else>
<img src="/images/graphic-5.png" alt="arrow-right" className="size-32 lg:size-40 mb-2" />
</Else>
</If>
</When>
{graph ? chart : null}
<Text variant="text-24-bold" className={classNames("flex w-full items-baseline", classNameLabelValue)}>
{value?.toLocaleString()}&nbsp;
<When condition={(!!limit || !!totalValue) && totalValue !== "no data"}>
<Text variant="text-16-light">of {limit?.toLocaleString() ?? totalValue?.toLocaleString()}</Text>
<When condition={!!limit || !!totalValue}>
<Text variant="text-16-light">
of {limit?.toLocaleString() ?? totalValue?.toLocaleString()} {hectares ? "ha" : null}
</Text>
</When>
<When condition={!!labelValue}>
<Text variant="text-16-light">{labelValue}</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ exports[`Storyshots Components/Elements/Cards/GoalProgressCard/Card Progress And
>
of
732

</p>
</p>
<div
Expand Down Expand Up @@ -193,6 +194,7 @@ exports[`Storyshots Components/Elements/Cards/GoalProgressCard/Card Progress No
>
of
150

</p>
</p>
<div
Expand Down
190 changes: 190 additions & 0 deletions src/pages/site/[uuid]/components/GoalsAndProgressSiteTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import { useT } from "@transifex/react";
import React from "react";

import ProgressGoalsDoughnutChart from "@/admin/components/ResourceTabs/MonitoredTab/components/ProgressGoalsDoughnutChart";
import GoalProgressCard from "@/components/elements/Cards/GoalProgressCard/GoalProgressCard";
import { IconNames } from "@/components/extensive/Icon/Icon";
import { ALL_TF, Framework } from "@/context/framework.provider";

interface GoalsAndProgressSiteTabProps {
site: any;
}
interface ProgressDataCardItem {
cardValues: {
label: string;
value: number;
totalName?: string;
totalValue?: number;
};
chartData: any;
graph?: boolean;
hectares?: boolean;
}

type ChartsData = {
terrafund: JSX.Element[];
ppc: JSX.Element[];
hbf: JSX.Element[];
};

const ProgressDataCard = (values: ProgressDataCardItem) => {
return (
<GoalProgressCard
label={values.cardValues.label}
value={values.cardValues.value}
totalValue={values.cardValues.totalValue}
hectares={values.hectares}
graph={values.graph}
classNameLabel="text-neutral-650 uppercase mb-3"
labelVariant="text-14"
classNameCard="text-center flex flex-col items-center"
classNameLabelValue="justify-center"
chart={<ProgressGoalsDoughnutChart key={"items"} data={values.chartData} />}
/>
);
};

const GoalsAndProgressSiteTab = ({ site }: GoalsAndProgressSiteTabProps) => {
const t = useT();
const totaTreesRestoredCount = site?.trees_planted_count + site?.regenerated_trees_count + site?.seeds_planted_count;
const chartDataHectares = {
chartData: [
{ name: t("HECTARES RESTORED"), value: site.total_hectares_restored_sum },
site.framework_key !== Framework.PPC
? {
name: t("TOTAL HECTARES RESTORED"),
value: parseFloat(site.hectares_to_restore_goal)
}
: {}
],
cardValues: {
label: t("HECTARES RESTORED"),
value: site.total_hectares_restored_sum,
totalName: t("TOTAL HECTARES RESTORED"),
totalValue: parseFloat(site.hectares_to_restore_goal)
}
};
const chartDataTreesRestored = {
chartData: [{ name: t("TREES RESTORED"), value: totaTreesRestoredCount }],
cardValues: {
label: t("TREES RESTORED"),
value: totaTreesRestoredCount
}
};
const chartDataWorkdays = {
chartData: [
{
name: t("WORKDAYS CREATED"),
value: site.framework_key == Framework.PPC ? site.combined_workday_count : site.workday_count
}
],
cardValues: {
label: t("WORKDAYS CREATED"),
value: site.framework_key == Framework.PPC ? site.combined_workday_count : site.workday_count
}
};
const chartDataSaplings = {
chartData: [{ name: t("SAPLINGS RESTORED"), value: totaTreesRestoredCount }],
cardValues: {
label: t("SAPLINGS RESTORED"),
value: totaTreesRestoredCount
}
};

const chartsDataMapping: ChartsData = {
terrafund: [
<ProgressDataCard
key={"terrafund-1"}
cardValues={chartDataHectares.cardValues}
chartData={chartDataHectares}
hectares={true}
/>,
<ProgressDataCard
key={"terrafund-2"}
cardValues={chartDataTreesRestored.cardValues}
chartData={chartDataTreesRestored}
graph={false}
/>
],
ppc: [
<ProgressDataCard
key={"ppc-1"}
cardValues={chartDataHectares.cardValues}
chartData={chartDataHectares}
graph={false}
hectares={true}
/>,
<ProgressDataCard
key={"ppc-2"}
cardValues={chartDataTreesRestored.cardValues}
chartData={chartDataTreesRestored}
graph={false}
/>,
<ProgressDataCard
key={"ppc-3"}
cardValues={chartDataWorkdays.cardValues}
chartData={chartDataWorkdays}
graph={false}
/>
],
hbf: [
<ProgressDataCard
key={"hbf-1"}
cardValues={chartDataWorkdays.cardValues}
chartData={chartDataWorkdays}
graph={false}
/>,
<ProgressDataCard
key={"hbf-2"}
cardValues={chartDataHectares.cardValues}
chartData={chartDataHectares}
hectares={true}
/>,
<ProgressDataCard
key={"hbf-3"}
cardValues={chartDataSaplings.cardValues}
chartData={chartDataSaplings}
graph={false}
/>
]
};
const framework = ALL_TF.includes(site.framework_key as Framework) ? "terrafund" : site.framework_key;
return (
<div className="flex w-full flex-wrap items-start justify-between gap-8">
{chartsDataMapping[framework as keyof ChartsData]?.map((chart, index) => (
<React.Fragment key={index}>{chart}</React.Fragment>
))}
<GoalProgressCard
label={t("Trees restored")}
value={totaTreesRestoredCount}
limit={site.trees_grown_goal}
hasProgress={false}
items={[
{
iconName: IconNames.TREE_CIRCLE_PD,
label: t("Trees Planted:"),
variantLabel: "text-14",
classNameLabel: " text-neutral-650 uppercase",
value: site.trees_planted_count
},
{
iconName: IconNames.LEAF_CIRCLE_PD,
label: t("Seeds Planted:"),
variantLabel: "text-14",
classNameLabel: " text-neutral-650 uppercase",
value: site.seeds_planted_count
},
{
iconName: IconNames.REFRESH_CIRCLE_PD,
label: t("Trees Regenerating:"),
variantLabel: "text-14",
classNameLabel: " text-neutral-650 uppercase",
value: site.regenerated_trees_count
}
]}
className="pr-[41px] lg:pr-[150px]"
/>
</div>
);
};
export default GoalsAndProgressSiteTab;
Loading

0 comments on commit ee9362e

Please sign in to comment.