EmergencyPage.tsx
1 import { useState } from 'react' 2 import { useAuthStore } from '../store/auth' 3 import type { Chain } from '../types/vote' 4 import EmergencyPanel from '../components/emergency/EmergencyPanel' 5 import DEQMemberList from '../components/emergency/DEQMemberList' 6 import EmergencyActionHistory from '../components/emergency/EmergencyActionHistory' 7 8 export default function EmergencyPage() { 9 const { isConnected, chain: userChain } = useAuthStore() 10 const [selectedChain, setSelectedChain] = useState<Chain>(userChain || 'alpha') 11 const [activeTab, setActiveTab] = useState<'actions' | 'members' | 'history'>('actions') 12 13 return ( 14 <div> 15 {/* Header */} 16 <div className="flex items-center justify-between mb-6"> 17 <div> 18 <h1 className="text-2xl font-bold text-red-700">Emergency System</h1> 19 <p className="text-gray-600 text-sm mt-1"> 20 DEQ-controlled emergency actions for critical situations 21 </p> 22 </div> 23 24 <select 25 value={selectedChain} 26 onChange={(e) => setSelectedChain(e.target.value as Chain)} 27 className="px-3 py-2 border rounded-lg text-sm" 28 > 29 <option value="alpha">Alpha Chain</option> 30 <option value="delta">Delta Chain</option> 31 </select> 32 </div> 33 34 {/* Warning Banner */} 35 <div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6"> 36 <div className="flex items-start gap-3"> 37 <WarningIcon className="h-6 w-6 text-red-600 flex-shrink-0" /> 38 <div> 39 <h3 className="font-semibold text-red-800"> 40 Emergency Actions Only 41 </h3> 42 <p className="text-sm text-red-700 mt-1"> 43 This system is for genuine emergencies that cannot wait for normal 44 governance processes. All actions are permanently recorded on-chain 45 and require multi-signature approval from DEQ members. 46 </p> 47 </div> 48 </div> 49 </div> 50 51 {/* Connection Warning */} 52 {!isConnected && ( 53 <div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6"> 54 <div className="flex items-center gap-2 text-yellow-800"> 55 <LockIcon className="h-5 w-5" /> 56 <span>Connect your wallet to interact with the emergency system</span> 57 </div> 58 </div> 59 )} 60 61 {/* Tab Navigation */} 62 <div className="border-b mb-6"> 63 <nav className="flex gap-6"> 64 <TabButton 65 active={activeTab === 'actions'} 66 onClick={() => setActiveTab('actions')} 67 > 68 Emergency Actions 69 </TabButton> 70 <TabButton 71 active={activeTab === 'members'} 72 onClick={() => setActiveTab('members')} 73 > 74 DEQ Members 75 </TabButton> 76 <TabButton 77 active={activeTab === 'history'} 78 onClick={() => setActiveTab('history')} 79 > 80 History 81 </TabButton> 82 </nav> 83 </div> 84 85 {/* Tab Content */} 86 {activeTab === 'actions' && <EmergencyPanel chain={selectedChain} />} 87 {activeTab === 'members' && <DEQMemberList chain={selectedChain} />} 88 {activeTab === 'history' && <EmergencyActionHistory chain={selectedChain} />} 89 90 {/* Info Box */} 91 <div className="mt-8 bg-gray-50 rounded-lg p-6"> 92 <h3 className="font-semibold mb-3">Emergency System Overview</h3> 93 <div className="grid md:grid-cols-3 gap-6 text-sm text-gray-600"> 94 <div> 95 <h4 className="font-medium text-gray-800 mb-2">DEQ (Delegated Emergency Quorum)</h4> 96 <ul className="space-y-1"> 97 <li>• 5 members nominated by governance</li> 98 <li>• 3 signatures required for actions</li> 99 <li>• Primary + backup member roles</li> 100 <li>• Can be revoked by governance vote</li> 101 </ul> 102 </div> 103 <div> 104 <h4 className="font-medium text-gray-800 mb-2">Available Actions</h4> 105 <ul className="space-y-1"> 106 <li>• Pause/Resume minting</li> 107 <li>• Emergency rollback</li> 108 <li>• Freeze accounts</li> 109 <li>• Pause governance</li> 110 <li>• Emergency upgrades</li> 111 </ul> 112 </div> 113 <div> 114 <h4 className="font-medium text-gray-800 mb-2">Offline Signing</h4> 115 <ul className="space-y-1"> 116 <li>• Supports air-gapped devices</li> 117 <li>• Generate payload for CLI</li> 118 <li>• Import signed response</li> 119 <li>• Maximum security for keys</li> 120 </ul> 121 </div> 122 </div> 123 </div> 124 </div> 125 ) 126 } 127 128 function TabButton({ 129 active, 130 onClick, 131 children, 132 }: { 133 active: boolean 134 onClick: () => void 135 children: React.ReactNode 136 }) { 137 return ( 138 <button 139 onClick={onClick} 140 className={`pb-3 text-sm font-medium border-b-2 transition-colors ${ 141 active 142 ? 'border-red-600 text-red-600' 143 : 'border-transparent text-gray-500 hover:text-gray-700' 144 }`} 145 > 146 {children} 147 </button> 148 ) 149 } 150 151 function WarningIcon({ className }: { className?: string }) { 152 return ( 153 <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor"> 154 <path 155 strokeLinecap="round" 156 strokeLinejoin="round" 157 strokeWidth={2} 158 d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" 159 /> 160 </svg> 161 ) 162 } 163 164 function LockIcon({ className }: { className?: string }) { 165 return ( 166 <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor"> 167 <path 168 strokeLinecap="round" 169 strokeLinejoin="round" 170 strokeWidth={2} 171 d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" 172 /> 173 </svg> 174 ) 175 }