3
1

Leaderboard.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import React from "react";
  2. import { Table } from "react-bootstrap";
  3. import { Member, ProposalDetail } from "../../types";
  4. interface CouncilMember {
  5. handle: string;
  6. votes: number;
  7. proposalCount: number;
  8. percentage: number;
  9. }
  10. interface CouncilVotes {
  11. proposalCount: number;
  12. members: CouncilMember[];
  13. }
  14. const LeaderBoard = (props: {
  15. proposals: ProposalDetail[];
  16. members: Member[];
  17. councils: number[][];
  18. cycle: number;
  19. }) => {
  20. const { cycle, councils, proposals } = props;
  21. const summarizeVotes = (id: number, propList: ProposalDetail[]) => {
  22. let votes = 0;
  23. propList.forEach((p) => {
  24. if (!p || !p.votesByMemberId) return;
  25. const vote = p.votesByMemberId.find((v) => v.memberId === id);
  26. if (vote && vote.vote !== `Reject`) votes++;
  27. });
  28. return votes;
  29. };
  30. let councilMembers: Member[] = [];
  31. const councilVotes: CouncilVotes[] = councils.map(
  32. (council, i: number): CouncilVotes => {
  33. const start = 57601 + i * cycle;
  34. const end = 57601 + (i + 1) * cycle;
  35. const proposalsRound = proposals.filter(
  36. (p) => p && p.createdAt > start && p.createdAt < end
  37. );
  38. const proposalCount = proposalsRound.length;
  39. const members: CouncilMember[] = council.map(
  40. (id: number): CouncilMember => {
  41. const member = props.members.find((m) => m.id === id);
  42. if (!member)
  43. return { handle: ``, votes: 0, proposalCount, percentage: 0 };
  44. councilMembers.find((m) => m.id === id) ||
  45. councilMembers.push(member);
  46. let votes = summarizeVotes(Number(member.id), proposalsRound);
  47. const percentage = Math.round((100 * votes) / proposalCount);
  48. return { handle: member.handle, votes, proposalCount, percentage };
  49. }
  50. );
  51. return { proposalCount, members };
  52. }
  53. );
  54. councilMembers = councilMembers
  55. .map((m) => {
  56. return { ...m, id: summarizeVotes(Number(m.id), props.proposals) };
  57. })
  58. .sort((a, b) => b.id - a.id);
  59. return (
  60. <div className={`text-light m-3`}>
  61. <h2 className="w-100 text-center text-light">Votes per Council Member</h2>
  62. <Table className={`text-light`}>
  63. <thead>
  64. <tr>
  65. <th>Council Member</th>
  66. <th>Total Votes</th>
  67. {councilVotes.map((c, i: number) => (
  68. <th key={`round-${i + 1}`}>Round {1 + i}</th>
  69. ))}
  70. </tr>
  71. </thead>
  72. <tbody>
  73. {councilMembers.map((member: Member) => (
  74. <MemberRow
  75. key={member.handle}
  76. member={member}
  77. votes={councilVotes}
  78. />
  79. ))}
  80. <tr>
  81. <td>Proposals</td>
  82. <td>{proposals.length}</td>
  83. {councilVotes.map((round, i: number) => (
  84. <td key={`round-${i + 1}`}>{round.proposalCount}</td>
  85. ))}
  86. </tr>
  87. </tbody>
  88. </Table>
  89. </div>
  90. );
  91. };
  92. const MemberRow = (props: { member: Member; votes: CouncilVotes[] }) => {
  93. const { votes } = props;
  94. const { handle } = props.member;
  95. let totalVotes = 0;
  96. let totalProposals = 0;
  97. votes.forEach((c) => {
  98. const m = c.members.find((member) => member.handle === handle);
  99. if (!m) return;
  100. totalVotes += m.votes;
  101. totalProposals += m.proposalCount;
  102. });
  103. const totalPercent = Math.round((100 * totalVotes) / totalProposals);
  104. return (
  105. <tr key={handle}>
  106. <td>{handle}</td>
  107. <td>
  108. {totalVotes} / {totalProposals} ({totalPercent}%)
  109. </td>
  110. {props.votes.map((c, round: number) => (
  111. <RoundVotes
  112. key={`round-${round + 1}-${handle}`}
  113. member={c.members.find((member) => member.handle === handle)}
  114. />
  115. ))}
  116. </tr>
  117. );
  118. };
  119. const RoundVotes = (props: {
  120. member?: { handle: string; votes: number; percentage: number };
  121. }) => {
  122. if (!props.member || props.member.handle === ``) return <td />;
  123. const { votes, percentage } = props.member;
  124. return (
  125. <td>
  126. {votes} ({percentage}%)
  127. </td>
  128. );
  129. };
  130. export default LeaderBoard;