From c1e54baa5b7fd9453a9f8127757fa65ad9b3b6ea Mon Sep 17 00:00:00 2001 From: yacong <43548488@qq.com> Date: Mon, 20 Apr 2026 18:59:13 +0800 Subject: [PATCH] feat(ui): add group analytics with recharts #69 --- package.json | 3 +- src/app/groups/[id]/page.tsx | 3 + src/components/GroupAnalytics.tsx | 139 ++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/components/GroupAnalytics.tsx diff --git a/package.json b/package.json index 7e788f6..0cb9614 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "@stellar/freighter-api": "^2.0.0", "next": "^14.2.0", "react": "^18.3.0", - "react-dom": "^18.3.0" + "react-dom": "^18.3.0", + "recharts": "^2.12.7" }, "devDependencies": { "@types/node": "^20.0.0", diff --git a/src/app/groups/[id]/page.tsx b/src/app/groups/[id]/page.tsx index 02ab880..bf757f7 100644 --- a/src/app/groups/[id]/page.tsx +++ b/src/app/groups/[id]/page.tsx @@ -4,6 +4,7 @@ import { Navbar } from "@/components/Navbar"; import { MemberList } from "@/components/MemberList"; import { RoundProgress } from "@/components/RoundProgress"; import { ContributeModal } from "@/components/ContributeModal"; +import { GroupAnalytics } from "@/components/GroupAnalytics"; import { useState } from "react"; import { formatAmount, GroupStatus } from "@sorosave/sdk"; @@ -62,6 +63,8 @@ export default function GroupDetailPage() { payoutOrder={group.payoutOrder} currentRound={group.currentRound} /> + +
diff --git a/src/components/GroupAnalytics.tsx b/src/components/GroupAnalytics.tsx new file mode 100644 index 0000000..990a869 --- /dev/null +++ b/src/components/GroupAnalytics.tsx @@ -0,0 +1,139 @@ +"use client"; + +import React from "react"; +import { + LineChart, + Line, + BarChart, + Bar, + PieChart, + Pie, + Cell, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer +} from "recharts"; + +// Mock data for demonstration +const mockContributionOverTime = [ + { name: "Week 1", amount: 4000 }, + { name: "Week 2", amount: 3000 }, + { name: "Week 3", amount: 5000 }, + { name: "Week 4", amount: 4500 }, + { name: "Week 5", amount: 6000 }, + { name: "Week 6", amount: 5500 }, +]; + +const mockPayoutDistribution = [ + { name: "Member A", payout: 8000 }, + { name: "Member B", payout: 5000 }, + { name: "Member C", payout: 2000 }, + { name: "Member D", payout: 1500 }, +]; + +const mockMemberParticipation = [ + { name: "Active", value: 400 }, + { name: "Occasional", value: 300 }, + { name: "Inactive", value: 100 }, +]; + +const COLORS = ["#0088FE", "#00C49F", "#FFBB28", "#FF8042", "#8884d8"]; + +export const GroupAnalytics = () => { + return ( +
+

+ Group Analytics +

+ +
+ {/* Contribution Amount Over Time */} +
+

+ Contributions Over Time +

+
+ + + + + + + + + + +
+
+ + {/* Payout Distribution Bar Chart */} +
+

+ Payout Distribution +

+
+ + + + + + + + + + +
+
+ + {/* Member Participation Pie Chart */} +
+

+ Member Participation +

+
+ + + + `${name}: ${(percent * 100).toFixed(0)}%` + } + outerRadius={100} + fill="#8884d8" + dataKey="value" + > + {mockMemberParticipation.map((entry, index) => ( + + ))} + + + + + +
+
+
+
+ ); +};