/ frontend / src / pages / EmergencyPage.tsx
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  }