|
1 |
| -import { useEffect, useMemo, useState } from "react"; |
| 1 | +import { useEffect, useState } from "react"; |
2 | 2 |
|
3 |
| -import { LoadingTable, NetworkTypeTabs, networkTypeTabs, rpcByNetworkType } from "~/components/shared"; |
| 3 | +import { NetworkTypeTabs, networkTypeTabs, rpcByNetworkType } from "~/components/shared"; |
4 | 4 |
|
5 |
| -function capitalizeFirstLetter(s: string) { |
6 |
| - return s.charAt(0).toUpperCase() + s.slice(1); |
| 5 | +interface Authorization { |
| 6 | + msg_url: string; |
| 7 | + authorized_policy: string; |
7 | 8 | }
|
8 | 9 |
|
9 |
| -function formatDate(dateString: string) { |
10 |
| - const options: Intl.DateTimeFormatOptions = { |
11 |
| - year: "numeric", |
12 |
| - month: "long", |
13 |
| - day: "numeric", |
| 10 | +interface AuthorizationResponse { |
| 11 | + authorization_list: { |
| 12 | + authorizations: Authorization[]; |
14 | 13 | };
|
15 |
| - return new Date(dateString).toLocaleDateString(undefined, options); |
16 | 14 | }
|
17 | 15 |
|
18 |
| -export const AdminPolicy = () => { |
19 |
| - const [mainnetAdminPolicies, setMainnetAdminPolicies] = useState<any[]>([]); |
20 |
| - const [testnetAdminPolicies, setTestnetAdminPolicies] = useState<any[]>([]); |
| 16 | +interface Policy { |
| 17 | + policy_type: string; |
| 18 | + address: string; |
| 19 | +} |
| 20 | + |
| 21 | +interface PoliciesResponse { |
| 22 | + policies: { |
| 23 | + items: Policy[]; |
| 24 | + }; |
| 25 | +} |
21 | 26 |
|
| 27 | +export const AdminPolicy = () => { |
| 28 | + const [authorizations, setAuthorizations] = useState<Authorization[]>([]); |
| 29 | + const [policies, setPolicies] = useState<Policy[]>([]); |
22 | 30 | const [activeTab, setActiveTab] = useState(networkTypeTabs[0]);
|
23 | 31 |
|
24 | 32 | useEffect(() => {
|
25 |
| - setMainnetAdminPolicies([]); |
26 |
| - setTestnetAdminPolicies([]); |
27 |
| - |
28 | 33 | const baseUrl = rpcByNetworkType[activeTab.networkType];
|
29 | 34 |
|
30 |
| - fetch(`${baseUrl}/zeta-chain/observer/params`) |
| 35 | + // Fetch authorizations |
| 36 | + fetch(`${baseUrl}/zeta-chain/authority/authorizations`) |
| 37 | + .then((response) => response.json()) |
| 38 | + .then((data: AuthorizationResponse) => { |
| 39 | + setAuthorizations(data.authorization_list.authorizations); |
| 40 | + }) |
| 41 | + .catch((error) => { |
| 42 | + console.error("Error fetching authorizations:", error); |
| 43 | + }); |
| 44 | + |
| 45 | + // Fetch policies |
| 46 | + fetch(`${baseUrl}/zeta-chain/authority/policies`) |
31 | 47 | .then((response) => response.json())
|
32 |
| - .then((data) => { |
33 |
| - const policies = data.params.admin_policy; |
34 |
| - policies.forEach((policy: any) => { |
35 |
| - fetch(`${baseUrl}/cosmos/group/v1/group_policy_info/${policy.address}`) |
36 |
| - .then((response) => response.json()) |
37 |
| - .then((detailData) => { |
38 |
| - fetch(`${baseUrl}/cosmos/group/v1/group_members/${detailData.info.group_id}`) |
39 |
| - .then((response) => response.json()) |
40 |
| - .then((membersData) => { |
41 |
| - if (activeTab.networkType === "mainnet") { |
42 |
| - setMainnetAdminPolicies((prevPolicies) => [ |
43 |
| - ...prevPolicies, |
44 |
| - { |
45 |
| - ...detailData.info, |
46 |
| - created_at: formatDate(detailData.info.created_at), |
47 |
| - policy_type: capitalizeFirstLetter(policy.policy_type), |
48 |
| - members: membersData.members.map((m: any) => ({ |
49 |
| - ...m.member, |
50 |
| - addedAt: formatDate(m.member.added_at), |
51 |
| - })), |
52 |
| - decision_policy: detailData.info.decision_policy, |
53 |
| - }, |
54 |
| - ]); |
55 |
| - } |
56 |
| - |
57 |
| - if (activeTab.networkType === "testnet") { |
58 |
| - setTestnetAdminPolicies((prevPolicies) => [ |
59 |
| - ...prevPolicies, |
60 |
| - { |
61 |
| - ...detailData.info, |
62 |
| - created_at: formatDate(detailData.info.created_at), |
63 |
| - policy_type: capitalizeFirstLetter(policy.policy_type), |
64 |
| - members: membersData.members.map((m: any) => ({ |
65 |
| - ...m.member, |
66 |
| - addedAt: formatDate(m.member.added_at), |
67 |
| - })), |
68 |
| - decision_policy: detailData.info.decision_policy, |
69 |
| - }, |
70 |
| - ]); |
71 |
| - } |
72 |
| - }); |
73 |
| - }); |
74 |
| - }); |
| 48 | + .then((data: PoliciesResponse) => { |
| 49 | + setPolicies(data.policies.items); |
75 | 50 | })
|
76 | 51 | .catch((error) => {
|
77 |
| - console.error("Error fetching admin policies:", error); |
| 52 | + console.error("Error fetching policies:", error); |
78 | 53 | });
|
79 | 54 | }, [activeTab.networkType]);
|
80 | 55 |
|
81 |
| - const adminPolicies = useMemo(() => { |
82 |
| - return activeTab.networkType === "mainnet" ? mainnetAdminPolicies : testnetAdminPolicies; |
83 |
| - }, [activeTab.networkType, mainnetAdminPolicies, testnetAdminPolicies]); |
| 56 | + const groupedAuthorizations = authorizations.reduce((acc, auth) => { |
| 57 | + const group = auth.authorized_policy; |
| 58 | + if (!acc[group]) { |
| 59 | + acc[group] = []; |
| 60 | + } |
| 61 | + const cleanMsg = auth.msg_url.replace("/zetachain.zetacore.", ""); |
| 62 | + acc[group].push(cleanMsg); |
| 63 | + return acc; |
| 64 | + }, {} as Record<string, string[]>); |
| 65 | + |
| 66 | + const getPolicyAddress = (policyType: string) => { |
| 67 | + const policy = policies.find((p) => p.policy_type === policyType); |
| 68 | + return policy?.address || ""; |
| 69 | + }; |
| 70 | + |
| 71 | + const formatGroupName = (group: string) => { |
| 72 | + return `Group Policy "${group.replace("group", "")}"`; |
| 73 | + }; |
| 74 | + |
| 75 | + const formatModuleName = (module: string) => { |
| 76 | + return `${module.charAt(0).toUpperCase() + module.slice(1)} Module`; |
| 77 | + }; |
| 78 | + |
| 79 | + const groupByModule = (messages: string[]) => { |
| 80 | + return messages.reduce((acc, msg) => { |
| 81 | + const module = msg.split(".")[0]; |
| 82 | + if (!acc[module]) { |
| 83 | + acc[module] = []; |
| 84 | + } |
| 85 | + acc[module].push(msg); |
| 86 | + return acc; |
| 87 | + }, {} as Record<string, string[]>); |
| 88 | + }; |
84 | 89 |
|
85 | 90 | return (
|
86 | 91 | <div className="mt-8 first:mt-0">
|
87 | 92 | <NetworkTypeTabs activeTab={activeTab} setActiveTab={setActiveTab} layoutIdPrefix="admin-policy-" />
|
88 | 93 |
|
89 |
| - {adminPolicies.length > 0 ? ( |
90 |
| - adminPolicies.map((policy: any, index) => ( |
91 |
| - // eslint-disable-next-line react/no-array-index-key |
92 |
| - <div key={index}> |
93 |
| - <h3 className="text-xl mt-8 font-medium">Policy: {policy?.policy_type}</h3> |
94 |
| - |
95 |
| - <div className="overflow-x-auto mt-8"> |
96 |
| - <table> |
97 |
| - <tbody> |
98 |
| - <tr> |
99 |
| - <td>Address</td> |
100 |
| - <td>{policy.address}</td> |
101 |
| - </tr> |
102 |
| - <tr> |
103 |
| - <td>Admin</td> |
104 |
| - <td>{policy.admin}</td> |
105 |
| - </tr> |
106 |
| - <tr> |
107 |
| - <td>Created At</td> |
108 |
| - <td>{policy.created_at}</td> |
109 |
| - </tr> |
110 |
| - <tr> |
111 |
| - <td>Group ID</td> |
112 |
| - <td>{policy.group_id}</td> |
113 |
| - </tr> |
114 |
| - <tr> |
115 |
| - <td>Metadata</td> |
116 |
| - <td>{policy.metadata}</td> |
117 |
| - </tr> |
118 |
| - <tr> |
119 |
| - <td>Decision Policy Type</td> |
120 |
| - <td>{policy.decision_policy["@type"].split("/").pop()}</td> |
121 |
| - </tr> |
122 |
| - <tr> |
123 |
| - <td>Threshold</td> |
124 |
| - <td>{policy.decision_policy.threshold}</td> |
125 |
| - </tr> |
126 |
| - <tr> |
127 |
| - <td>Min Execution Period</td> |
128 |
| - <td>{policy.decision_policy.windows.min_execution_period}</td> |
129 |
| - </tr> |
130 |
| - <tr> |
131 |
| - <td>Voting Period</td> |
132 |
| - <td>{policy.decision_policy.windows.voting_period}</td> |
133 |
| - </tr> |
134 |
| - </tbody> |
135 |
| - </table> |
136 |
| - </div> |
137 |
| - |
138 |
| - <h3 className="text-xl mt-8 font-medium">Members</h3> |
139 |
| - |
140 |
| - <div className="overflow-x-auto mt-8"> |
141 |
| - <table> |
142 |
| - <thead> |
143 |
| - <tr> |
144 |
| - <th>Added At</th> |
145 |
| - <th>Address</th> |
146 |
| - <th>Metadata</th> |
147 |
| - <th>Weight</th> |
148 |
| - </tr> |
149 |
| - </thead> |
150 |
| - <tbody> |
151 |
| - {policy.members.map((member: any, memberIndex: number) => ( |
152 |
| - // eslint-disable-next-line react/no-array-index-key |
153 |
| - <tr key={memberIndex}> |
154 |
| - <td>{member.addedAt}</td> |
155 |
| - <td>{member.address}</td> |
156 |
| - <td>{member.metadata}</td> |
157 |
| - <td>{member.weight}</td> |
158 |
| - </tr> |
159 |
| - ))} |
160 |
| - </tbody> |
161 |
| - </table> |
| 94 | + <div className="mt-8 space-y-8"> |
| 95 | + {Object.entries(groupedAuthorizations).map(([group, messages]) => { |
| 96 | + const moduleGroups = groupByModule(messages); |
| 97 | + return ( |
| 98 | + <div key={group}> |
| 99 | + <h2 className="text-2xl font-medium mb-2">{formatGroupName(group)}</h2> |
| 100 | + <div className="text-sm text-gray-500 mb-4">Address: {getPolicyAddress(group)}</div> |
| 101 | + <div className="space-y-4"> |
| 102 | + {Object.entries(moduleGroups).map(([module, msgs]) => ( |
| 103 | + <div key={module}> |
| 104 | + <h3 className="text-lg font-medium mb-2">{formatModuleName(module)}</h3> |
| 105 | + <ul className="list-disc list-inside space-y-1"> |
| 106 | + {msgs.map((msg, index) => ( |
| 107 | + <li key={index} className="text-sm"> |
| 108 | + {msg.split(".")[1]} |
| 109 | + </li> |
| 110 | + ))} |
| 111 | + </ul> |
| 112 | + </div> |
| 113 | + ))} |
| 114 | + </div> |
162 | 115 | </div>
|
163 |
| - </div> |
164 |
| - )) |
165 |
| - ) : ( |
166 |
| - <LoadingTable rowCount={9} /> |
167 |
| - )} |
| 116 | + ); |
| 117 | + })} |
| 118 | + </div> |
168 | 119 | </div>
|
169 | 120 | );
|
170 | 121 | };
|
0 commit comments