Files
smom-dbis-138/frontend-dapp/src/components/admin/AdminDashboard.tsx
zaragoza444 cbd669c762
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m41s
CI/CD Pipeline / Security Scanning (push) Successful in 2m32s
CI/CD Pipeline / Lint and Format (push) Failing after 51s
CI/CD Pipeline / Terraform Validation (push) Failing after 35s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 30s
Validation / validate-genesis (push) Successful in 34s
Validation / validate-terraform (push) Failing after 36s
Validation / validate-kubernetes (push) Failing after 11s
Validation / validate-smart-contracts (push) Failing after 11s
Validation / validate-security (push) Failing after 1m23s
Validation / validate-documentation (push) Failing after 21s
feat: NASA mission-control theme for all OMNL dashboards
Replace Binance-style dashboard chrome with a shared telemetry UI, SVG icons, and mission headers across Central Bank, Office 24, trade, swap, and admin screens.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 17:10:24 -07:00

177 lines
7.1 KiB
TypeScript

/**
* AdminDashboard Component - Analytics and monitoring dashboard
*/
import { useState, useEffect } from 'react'
import { useAccount, usePublicClient } from 'wagmi'
import { useAdmin } from '../../contexts/AdminContext'
import { TransactionRequestStatus } from '../../types/admin'
import { CONTRACT_ADDRESSES } from '../../config/contracts'
import { MAINNET_TETHER_ABI } from '../../abis/MainnetTether'
import { subscribeToContractEvents } from '../../utils/contractEvents'
import toast from 'react-hot-toast'
import DashboardStatCard from '../../features/omnl-dashboard/DashboardStatCard'
import DashboardSection from '../../features/omnl-dashboard/DashboardSection'
import NasaIcon, { StatusIcon } from '../icons/NasaIcon'
export default function AdminDashboard() {
const { adminActions, auditLogs } = useAdmin()
const { address } = useAccount()
const publicClient = usePublicClient()
const [mainnetTetherPaused, setMainnetTetherPaused] = useState<boolean>(false)
const stats = {
totalActions: adminActions.length,
pending: adminActions.filter((a) => a.status === TransactionRequestStatus.PENDING).length,
approved: adminActions.filter((a) => a.status === TransactionRequestStatus.APPROVED).length,
executed: adminActions.filter((a) => a.status === TransactionRequestStatus.SUCCESS).length,
failed: adminActions.filter((a) => a.status === TransactionRequestStatus.FAILED).length,
totalAuditLogs: auditLogs.length,
successRate:
adminActions.length > 0
? (
(adminActions.filter((a) => a.status === TransactionRequestStatus.SUCCESS).length /
adminActions.length) *
100
).toFixed(1)
: '0',
}
const recentActions = adminActions.slice(-5).reverse()
const recentLogs = auditLogs.slice(-5).reverse()
useEffect(() => {
if (publicClient && address) {
// Fetch contract states
const fetchStates = async () => {
try {
// Fetch paused state for MainnetTether
const paused = await publicClient.readContract({
address: CONTRACT_ADDRESSES.mainnet.MAINNET_TETHER,
abi: MAINNET_TETHER_ABI,
functionName: 'paused',
})
setMainnetTetherPaused(paused as boolean)
} catch (error) {
console.error('Error fetching contract states:', error)
}
}
fetchStates()
const interval = setInterval(fetchStates, 30000) // Refresh every 30 seconds
// Subscribe to contract events for real-time updates
let unsubscribePaused: (() => void) | null = null
let unsubscribeUnpaused: (() => void) | null = null
subscribeToContractEvents(
publicClient,
CONTRACT_ADDRESSES.mainnet.MAINNET_TETHER,
MAINNET_TETHER_ABI,
'Paused',
(_event) => {
setMainnetTetherPaused(true)
toast.success('MainnetTether paused event detected')
}
).then((unsub) => {
unsubscribePaused = unsub
}).catch(() => {})
subscribeToContractEvents(
publicClient,
CONTRACT_ADDRESSES.mainnet.MAINNET_TETHER,
MAINNET_TETHER_ABI,
'Unpaused',
(_event) => {
setMainnetTetherPaused(false)
toast.success('MainnetTether unpaused event detected')
}
).then((unsub) => {
unsubscribeUnpaused = unsub
}).catch(() => {})
return () => {
clearInterval(interval)
if (unsubscribePaused) unsubscribePaused()
if (unsubscribeUnpaused) unsubscribeUnpaused()
}
}
}, [publicClient, address])
return (
<div className="space-y-6 nasa-mission-root">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<DashboardStatCard label="Total actions" value={String(stats.totalActions)} accent="blue" icon="admin" />
<DashboardStatCard label="Pending" value={String(stats.pending)} accent="gold" icon="alert" />
<DashboardStatCard label="Executed" value={String(stats.executed)} accent="green" icon="check" />
<DashboardStatCard label="Success rate" value={`${stats.successRate}%`} accent="blue" icon="chart" />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="dash-card">
<div className="flex items-center justify-between mb-2">
<div className="dash-stat-label flex items-center gap-1">
<NasaIcon name="bridge" size={14} className="nasa-icon--telemetry" />
MainnetTether
</div>
<span className={`nasa-badge ${mainnetTetherPaused ? 'nasa-badge--caution' : 'nasa-badge--nominal'}`}>
{mainnetTetherPaused ? 'Paused' : 'Active'}
</span>
</div>
<p className="text-[#7a9bb8] text-xs font-mono">{CONTRACT_ADDRESSES.mainnet.MAINNET_TETHER}</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<DashboardSection title="Recent actions" icon="rocket">
{recentActions.length === 0 ? (
<p className="text-[#7a9bb8] text-sm">No actions yet</p>
) : (
<div className="space-y-3">
{recentActions.map((action) => (
<div key={action.id} className="nasa-inset-panel">
<div className="flex justify-between items-start">
<div>
<p className="text-[#e8f4ff] font-semibold text-sm font-mono">{action.type}</p>
<p className="text-[#7a9bb8] text-xs font-mono">{action.contractAddress.slice(0, 20)}...</p>
</div>
<span className="flex items-center gap-1 text-xs font-mono">
<StatusIcon ok={action.status === TransactionRequestStatus.SUCCESS} />
{action.status}
</span>
</div>
<p className="text-[#7a9bb8] text-xs mt-2">{new Date(action.createdAt).toLocaleString()}</p>
</div>
))}
</div>
)}
</DashboardSection>
<DashboardSection title="Recent audit logs" icon="ledger">
{recentLogs.length === 0 ? (
<p className="text-[#7a9bb8] text-sm">No audit logs yet</p>
) : (
<div className="space-y-3">
{recentLogs.map((log) => (
<div key={log.id} className="nasa-inset-panel">
<div className="flex justify-between items-start">
<div>
<p className="text-[#e8f4ff] font-semibold text-sm font-mono">{log.action}</p>
<p className="text-[#7a9bb8] text-xs font-mono">{log.user.slice(0, 10)}...</p>
</div>
<span className="flex items-center gap-1 text-xs font-mono">
<StatusIcon ok={log.status === 'success'} />
{log.status}
</span>
</div>
<p className="text-[#7a9bb8] text-xs mt-2">{new Date(log.timestamp).toLocaleString()}</p>
</div>
))}
</div>
)}
</DashboardSection>
</div>
</div>
)
}