Skip to content

Commit

Permalink
Merge branch 'community-and-pools-metrics' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Mati0x committed Jan 24, 2025
2 parents e7a172b + 5b4b767 commit 8541c84
Show file tree
Hide file tree
Showing 13 changed files with 540 additions and 228 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
EthAddress,
InfoBox,
Statistic,
DataTable,
} from "@/components";
import CancelButton from "@/components/CancelButton";
import { ConvictionBarChart } from "@/components/Charts/ConvictionBarChart";
Expand All @@ -41,7 +42,7 @@ import { ConditionObject, useDisableButtons } from "@/hooks/useDisableButtons";
import { useMetadataIpfsFetch } from "@/hooks/useIpfsFetch";
import { useSubgraphQuery } from "@/hooks/useSubgraphQuery";
import { alloABI } from "@/src/generated";
import { PoolTypes, ProposalStatus } from "@/types";
import { PoolTypes, ProposalStatus, Column } from "@/types";

import { useErrorDetails } from "@/utils/getErrorName";
import { calculatePercentageBigInt } from "@/utils/numbers";
Expand All @@ -51,6 +52,7 @@ type ProposalSupporter = {
id: string;
stakes: { amount: number }[];
};
type SupporterColumn = Column<ProposalSupporter>;

export default function Page({
params: { proposalId, garden, community: communityAddr, poolId },
Expand Down Expand Up @@ -111,10 +113,14 @@ export default function Page({
const proposalData = data?.cvproposal;
const proposalSupporters = supportersData?.members;

const filteredAndSortedProposalSupporters =
const filteredAndSortedProposalSupporters: ProposalSupporter[] =
proposalSupporters ?
proposalSupporters
.filter((item) => item.stakes && item.stakes.length > 0)
.map((item) => ({
id: item.id,
stakes: item.stakes?.map((stake) => ({ amount: stake.amount })) ?? [],
}))
.sort((a, b) => {
const maxStakeA = Math.max(
...(a.stakes ?? []).map((stake) => stake.amount),
Expand Down Expand Up @@ -415,123 +421,77 @@ export default function Page({
)}
{filteredAndSortedProposalSupporters.length > 0 && (
<ProposalSupportersTable
_proposalSupporters={
filteredAndSortedProposalSupporters as ProposalSupporter[]
}
_totalActivePoints={totalEffectiveActivePoints}
_totalStakedAmount={totalSupportPct}
_beneficiary={beneficiary}
_submitter={submitter}
supporters={filteredAndSortedProposalSupporters}
beneficiary={beneficiary}
submitter={submitter}
totalActivePoints={totalEffectiveActivePoints}
totalStakedAmount={totalSupportPct}
/>
)}
</div>
);
}

function ProposalSupportersTable({
_proposalSupporters,
_totalActivePoints,
_totalStakedAmount,
_beneficiary,
_submitter,
const ProposalSupportersTable = ({
supporters,
beneficiary,
submitter,
totalActivePoints,
totalStakedAmount,
}: {
_proposalSupporters: ProposalSupporter[];
_totalActivePoints: number;
_totalStakedAmount: number;
_beneficiary: string | undefined;
_submitter: string | undefined;
}) {
supporters: ProposalSupporter[];
beneficiary: string | undefined;
submitter: string | undefined;
totalActivePoints: number;
totalStakedAmount: number;
}) => {
const columns: SupporterColumn[] = [
{
header: supporters.length > 1 ? "Supporters" : "Supporter",
render: (supporter: ProposalSupporter) => (
<EthAddress
address={supporter.id as Address}
actions="copy"
shortenAddress={false}
icon="ens"
/>
),
},
{
header: "Role",
render: (supporter: ProposalSupporter) =>
supporter.id === beneficiary ? "Beneficiary"
: supporter.id === submitter ? "Submitter"
: "Member",
},
{
header: "Support",
render: (supporter: ProposalSupporter) =>
totalActivePoints > 0 ?
`${calculatePercentageBigInt(
BigInt(supporter?.stakes[0]?.amount),
BigInt(totalActivePoints),
)} %`
: undefined,
className: "text-center",
},
];

return (
<div className="px-2 section-layout">
<div className="sm:flex sm:items-center">
<div className="sm:flex-auto">
<h3>Supported By</h3>
<p className="mt-2 text-sm text-neutral-soft-content">
A list of all the community members that are supporting this
proposal.
<DataTable
title="Supported By"
description="A list of all the community members that are supporting this proposal."
data={supporters}
columns={columns}
footer={
//
<div className="flex justify-between py-2 border-neutral-soft-content">
<p className="subtitle">Total Support:</p>
<p className="subtitle pr-0 sm:pr-14 lg:pr-16">
{totalStakedAmount} %
</p>
</div>
</div>
<div className="mt-8 flow-root">
<div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
<table className="min-w-full divide-y divide-neutral-soft">
<thead>
<tr>
<th scope="col" className="py-3.5 pl-4 pr-3 sm:pl-0">
<h5>
{_proposalSupporters.length > 1 ?
"Supporters"
: "Supporter"}
</h5>
</th>
<th scope="col" className="px-3 py-3.5">
<h5>Role</h5>
</th>
<th scope="col" className="px-3 py-3.5 text-center">
<h5>Support</h5>
</th>
</tr>
</thead>
<tbody className="divide-y divide-neutral-soft">
{_proposalSupporters.map((supporter: ProposalSupporter) => (
<tr key={supporter.id}>
<td className="whitespace-nowrap py-5 pl-4 pr-3 sm:pl-0 text-sm text-neutral-soft-content">
<div className="flex items-center">
<div className="ml-4">
<EthAddress
address={supporter.id as Address}
actions="copy"
shortenAddress={false}
icon={"ens"}
/>
</div>
</div>
</td>
{/* members role */}
<td className="whitespace-nowrap px-3 py-5 text-sm text-neutral-soft-content">
<p>
{supporter.id === _beneficiary ?
"Beneficiary"
: supporter.id === _submitter ?
"Submitter"
: "Member"}
</p>
</td>
{/* members support */}
<td className="whitespace-nowrap px-3 py-5 text-sm text-neutral-soft-content">
<p className="subtitle">
{(_totalActivePoints ?? 0) > 0 ?
calculatePercentageBigInt(
BigInt(supporter?.stakes[0]?.amount),
BigInt(_totalActivePoints ?? 0),
)
: undefined}{" "}
%
</p>{" "}
</td>
</tr>
))}
</tbody>
<tfoot>
<tr>
<th
scope="col"
colSpan={2}
className="pl-8 pr-3 pt-4 sm:table-cell sm:pl-0"
>
<p className="subtitle">Total Support:</p>
</th>

<td className="pl-3 pr-4 pt-4 text-left sm:pr-0">
<p className="subtitle">{_totalStakedAmount} %</p>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
}
/>
);
}
};
86 changes: 85 additions & 1 deletion apps/web/app/(app)/gardens/[chain]/[garden]/[community]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
RectangleGroupIcon,
UserGroupIcon,
} from "@heroicons/react/24/outline";

import { FetchTokenResult } from "@wagmi/core";
import { Dnum, multiply } from "dnum";
import Image from "next/image";
import Link from "next/link";
Expand All @@ -28,6 +30,7 @@ import {
RegisterMember,
Statistic,
InfoWrapper,
DataTable,
} from "@/components";
import { LoadingSpinner } from "@/components/LoadingSpinner";
import MarkdownWrapper from "@/components/MarkdownWrapper";
Expand All @@ -39,14 +42,27 @@ import { useCollectQueryParams } from "@/contexts/collectQueryParams.context";
import { useDisableButtons } from "@/hooks/useDisableButtons";
import { useSubgraphQuery } from "@/hooks/useSubgraphQuery";
import { safeABI } from "@/src/generated";
import { PoolTypes } from "@/types";
import { PoolTypes, Column } from "@/types";
import { fetchIpfs } from "@/utils/ipfsUtils";
import {
parseToken,
SCALE_PRECISION,
SCALE_PRECISION_DECIMALS,
} from "@/utils/numbers";

type MembersStaked = {
memberAddress: string;
stakedTokens: string;
};

type CommunityMetricsProps = {
membersStaked: MembersStaked[] | undefined;
tokenGarden: FetchTokenResult;
communityStakedTokens: number | bigint;
};

type MemberColumn = Column<MembersStaked>;

export default function Page({
params: { chain, garden: tokenAddr, community: communityAddr },
}: {
Expand All @@ -55,6 +71,8 @@ export default function Page({
const searchParams = useCollectQueryParams();
const { address: accountAddress } = useAccount();
const [covenant, setCovenant] = useState<string | undefined>();
const [openCommDetails, setOpenCommDetails] = useState(false);

const covenantSectionRef = useRef<HTMLDivElement>(null);
const { data: tokenGarden } = useToken({
address: tokenAddr as Address,
Expand Down Expand Up @@ -278,6 +296,13 @@ export default function Page({
height={180}
width={180}
/>
<Button
onClick={() => setOpenCommDetails(!openCommDetails)}
btnStyle="outline"
className="mt-1"
>
{openCommDetails ? "Close" : "View"} Members
</Button>
</div>
<div className="flex flex-1 flex-col gap-2">
<div>
Expand Down Expand Up @@ -330,7 +355,15 @@ export default function Page({
registryCommunity={registryCommunity}
/>
</div>
{openCommDetails && (
<CommunityDetailsTable
membersStaked={registryCommunity.members as MembersStaked[]}
tokenGarden={tokenGarden}
communityStakedTokens={communityStakedTokens}
/>
)}
</header>

<IncreasePower
memberData={isMemberResult}
registryCommunity={registryCommunity}
Expand Down Expand Up @@ -437,3 +470,54 @@ export default function Page({
</div>
);
}

const CommunityDetailsTable = ({
membersStaked,
tokenGarden,
communityStakedTokens,
}: CommunityMetricsProps) => {
const columns: MemberColumn[] = [
{
header: `Members (${membersStaked?.length})`,
render: (memberData: MembersStaked) => (
<EthAddress
address={memberData.memberAddress as Address}
actions="copy"
shortenAddress={false}
icon="ens"
/>
),
},
{
header: "Staked tokens",
render: (memberData: MembersStaked) => (
<DisplayNumber
number={[BigInt(memberData.stakedTokens), tokenGarden.decimals]}
compact={true}
tokenSymbol={tokenGarden.symbol}
/>
),
className: "flex justify-end",
},
];

return (
<DataTable
title="Community Members"
data={membersStaked as MembersStaked[]}
description="Overview of all community members and the total amount of tokens they have staked."
columns={columns}
className="max-h-screen overflow-y-scroll w-full"
footer={
<div className="flex justify-between py-2 border-neutral-soft-content">
<p className="subtitle">Total Staked:</p>
<DisplayNumber
number={[BigInt(communityStakedTokens), tokenGarden.decimals]}
compact={true}
tokenSymbol={tokenGarden.symbol}
/>
</div>
}
/>
);
};
Loading

0 comments on commit 8541c84

Please sign in to comment.