Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TM-1605] site landing page progress goals #858

Merged
merged 15 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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