/ src / pages / GovernorsPage.tsx
GovernorsPage.tsx
  1  /**
  2   * Governors Page - Governor management for Central Bank
  3   */
  4  import { useEffect, useState } from 'react';
  5  import { useGovernanceStore } from '../store/governance';
  6  
  7  export function GovernorsPage() {
  8    const {
  9      governors,
 10      isLoading,
 11      fetchGovernors,
 12      proposeAddGovernor,
 13      proposeRemoveGovernor,
 14    } = useGovernanceStore();
 15  
 16    const [showAddModal, setShowAddModal] = useState(false);
 17    const [newGovernorAddress, setNewGovernorAddress] = useState('');
 18    const [newGovernorName, setNewGovernorName] = useState('');
 19  
 20    useEffect(() => {
 21      fetchGovernors();
 22    }, [fetchGovernors]);
 23  
 24    const handleAddGovernor = async (e: React.FormEvent) => {
 25      e.preventDefault();
 26      if (!newGovernorAddress || !newGovernorName) return;
 27  
 28      await proposeAddGovernor(newGovernorAddress, newGovernorName);
 29      setNewGovernorAddress('');
 30      setNewGovernorName('');
 31      setShowAddModal(false);
 32    };
 33  
 34    const formatVotingPower = (power: bigint): string => {
 35      const num = Number(power) / 1e18;
 36      return num.toLocaleString(undefined, { maximumFractionDigits: 2 });
 37    };
 38  
 39    const getTotalVotingPower = (): bigint => {
 40      return governors.reduce((sum, g) => sum + g.votingPower, BigInt(0));
 41    };
 42  
 43    const getVotingPowerPercentage = (power: bigint): number => {
 44      const total = getTotalVotingPower();
 45      if (total === BigInt(0)) return 0;
 46      return (Number(power) / Number(total)) * 100;
 47    };
 48  
 49    if (isLoading) {
 50      return (
 51        <div className="flex items-center justify-center h-64">
 52          <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-alpha" />
 53        </div>
 54      );
 55    }
 56  
 57    return (
 58      <div className="max-w-6xl mx-auto p-6">
 59        <div className="flex justify-between items-center mb-8">
 60          <h1 className="text-2xl font-bold text-text-primary">Governor Management</h1>
 61          <button
 62            onClick={() => setShowAddModal(true)}
 63            className="px-4 py-2 bg-alpha text-text-primary rounded-lg hover:bg-alpha-dark transition-colors"
 64          >
 65            Propose New Governor
 66          </button>
 67        </div>
 68  
 69        {/* Current Governors List */}
 70        <section className="mb-8">
 71          <h2 className="text-lg font-semibold text-text-primary mb-4">
 72            Current Governors
 73            <span className="ml-2 px-2 py-0.5 text-xs bg-alpha text-text-primary rounded-full">
 74              {governors.length}
 75            </span>
 76          </h2>
 77          <div className="bg-card rounded-xl overflow-hidden">
 78            {governors.length === 0 ? (
 79              <div className="p-6 text-center text-text-secondary">
 80                No governors configured
 81              </div>
 82            ) : (
 83              <table className="w-full">
 84                <thead className="bg-card-hover">
 85                  <tr>
 86                    <th className="text-left px-6 py-3 text-text-secondary text-sm font-medium">Name</th>
 87                    <th className="text-left px-6 py-3 text-text-secondary text-sm font-medium">Address</th>
 88                    <th className="text-left px-6 py-3 text-text-secondary text-sm font-medium">Voting Power</th>
 89                    <th className="text-left px-6 py-3 text-text-secondary text-sm font-medium">Status</th>
 90                    <th className="text-left px-6 py-3 text-text-secondary text-sm font-medium">Last Active</th>
 91                    <th className="text-right px-6 py-3 text-text-secondary text-sm font-medium">Actions</th>
 92                  </tr>
 93                </thead>
 94                <tbody className="divide-y divide-border">
 95                  {governors.map((governor) => (
 96                    <tr key={governor.address} className="hover:bg-card-hover">
 97                      <td className="px-6 py-4">
 98                        <div className="flex items-center gap-3">
 99                          <div className="w-10 h-10 rounded-full bg-gradient-to-br from-alpha to-alpha-dark flex items-center justify-center text-text-primary font-bold">
100                            {governor.name.charAt(0).toUpperCase()}
101                          </div>
102                          <span className="text-text-primary font-medium">{governor.name}</span>
103                        </div>
104                      </td>
105                      <td className="px-6 py-4 text-text-secondary font-mono text-sm">
106                        {governor.address.slice(0, 10)}...{governor.address.slice(-8)}
107                      </td>
108                      <td className="px-6 py-4">
109                        <div className="flex items-center gap-2">
110                          <span className="text-text-primary">{formatVotingPower(governor.votingPower)}</span>
111                          <span className="text-text-secondary text-sm">
112                            ({getVotingPowerPercentage(governor.votingPower).toFixed(1)}%)
113                          </span>
114                        </div>
115                      </td>
116                      <td className="px-6 py-4">
117                        <span className={`px-2 py-1 text-xs rounded-full ${
118                          governor.isActive ? 'bg-success/20 text-success' : 'bg-card-hover text-text-secondary'
119                        }`}>
120                          {governor.isActive ? 'Active' : 'Inactive'}
121                        </span>
122                      </td>
123                      <td className="px-6 py-4 text-text-secondary">
124                        {new Date(governor.lastActiveAt).toLocaleDateString()}
125                      </td>
126                      <td className="px-6 py-4">
127                        <div className="flex justify-end gap-2">
128                          <button
129                            onClick={() => proposeRemoveGovernor(governor.address)}
130                            className="px-3 py-1 bg-error/20 text-error text-sm rounded hover:bg-error/30"
131                          >
132                            Propose Removal
133                          </button>
134                        </div>
135                      </td>
136                    </tr>
137                  ))}
138                </tbody>
139              </table>
140            )}
141          </div>
142        </section>
143  
144        {/* Voting Power Distribution */}
145        <section className="mb-8">
146          <h2 className="text-lg font-semibold text-text-primary mb-4">Voting Power Distribution</h2>
147          <div className="bg-card rounded-xl p-6">
148            <div className="space-y-4">
149              {governors.map((governor) => (
150                <div key={governor.address}>
151                  <div className="flex justify-between text-sm mb-1">
152                    <span className="text-text-primary">{governor.name}</span>
153                    <span className="text-text-secondary">{getVotingPowerPercentage(governor.votingPower).toFixed(1)}%</span>
154                  </div>
155                  <div className="h-3 bg-card-hover rounded-full overflow-hidden">
156                    <div
157                      className="h-full bg-gradient-to-r from-alpha to-alpha-dark"
158                      style={{ width: `${getVotingPowerPercentage(governor.votingPower)}%` }}
159                    />
160                  </div>
161                </div>
162              ))}
163            </div>
164            <div className="mt-6 pt-4 border-t border-border">
165              <div className="flex justify-between text-sm">
166                <span className="text-text-secondary">Total Voting Power</span>
167                <span className="text-text-primary font-medium">{formatVotingPower(getTotalVotingPower())} ACDC</span>
168              </div>
169            </div>
170          </div>
171        </section>
172  
173        {/* Governor Activity Metrics */}
174        <section>
175          <h2 className="text-lg font-semibold text-text-primary mb-4">Governor Activity Metrics</h2>
176          <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
177            {governors.map((governor) => (
178              <div key={governor.address} className="bg-card rounded-xl p-6">
179                <div className="flex items-center gap-3 mb-4">
180                  <div className="w-10 h-10 rounded-full bg-gradient-to-br from-alpha to-alpha-dark flex items-center justify-center text-text-primary font-bold">
181                    {governor.name.charAt(0).toUpperCase()}
182                  </div>
183                  <span className="text-text-primary font-medium">{governor.name}</span>
184                </div>
185                <div className="space-y-3">
186                  <div className="flex justify-between">
187                    <span className="text-text-secondary text-sm">Proposals Voted</span>
188                    <span className="text-text-primary">{governor.proposalsVoted}</span>
189                  </div>
190                  <div className="flex justify-between">
191                    <span className="text-text-secondary text-sm">Proposals Created</span>
192                    <span className="text-text-primary">{governor.proposalsCreated}</span>
193                  </div>
194                  <div className="flex justify-between">
195                    <span className="text-text-secondary text-sm">Participation Rate</span>
196                    <span className="text-text-primary">{governor.participationRate.toFixed(1)}%</span>
197                  </div>
198                </div>
199              </div>
200            ))}
201          </div>
202        </section>
203  
204        {/* Add Governor Modal */}
205        {showAddModal && (
206          <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
207            <div className="bg-card rounded-xl p-6 w-full max-w-md">
208              <h3 className="text-xl font-bold text-text-primary mb-4">Propose New Governor</h3>
209              <form onSubmit={handleAddGovernor}>
210                <div className="mb-4">
211                  <label className="block text-text-secondary text-sm mb-2">Governor Name</label>
212                  <input
213                    type="text"
214                    value={newGovernorName}
215                    onChange={(e) => setNewGovernorName(e.target.value)}
216                    className="w-full bg-card-hover text-text-primary px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-alpha"
217                    placeholder="Central Bank Representative"
218                    required
219                  />
220                </div>
221                <div className="mb-6">
222                  <label className="block text-text-secondary text-sm mb-2">Wallet Address</label>
223                  <input
224                    type="text"
225                    value={newGovernorAddress}
226                    onChange={(e) => setNewGovernorAddress(e.target.value)}
227                    className="w-full bg-card-hover text-text-primary px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-alpha"
228                    placeholder="dx1..."
229                    required
230                  />
231                </div>
232                <p className="text-text-secondary text-sm mb-6">
233                  This will create a governance proposal to add a new governor. Other governors must approve this proposal for it to take effect.
234                </p>
235                <div className="flex gap-3">
236                  <button
237                    type="button"
238                    onClick={() => setShowAddModal(false)}
239                    className="flex-1 py-2 bg-card-hover text-text-primary rounded-lg hover:bg-border-strong"
240                  >
241                    Cancel
242                  </button>
243                  <button
244                    type="submit"
245                    className="flex-1 py-2 bg-alpha text-text-primary rounded-lg hover:bg-alpha-dark"
246                  >
247                    Create Proposal
248                  </button>
249                </div>
250              </form>
251            </div>
252          </div>
253        )}
254      </div>
255    );
256  }