feat(admin): refactor admin console to focus on superadmin tier, graduation and dispute governance
This commit is contained in:
@@ -3,13 +3,12 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Web3Provider } from '../context/Web3Context';
|
||||
import { AdminNavbar } from '../features/common/AdminNavbar';
|
||||
import { FaucetPanel } from '../features/faucet/FaucetPanel';
|
||||
import { MarketWizard } from '../features/wizard/MarketWizard';
|
||||
import { GovernancePanel } from '../features/governance/GovernancePanel';
|
||||
import { AdjudicationPanel } from '../features/adjudication/AdjudicationPanel';
|
||||
import { FaucetPanel } from '../features/faucet/FaucetPanel';
|
||||
|
||||
export default function AdminPage() {
|
||||
const [activeTab, setActiveTab] = useState<string>('faucet');
|
||||
const [activeTab, setActiveTab] = useState<string>('governance');
|
||||
|
||||
return (
|
||||
<Web3Provider>
|
||||
@@ -17,14 +16,13 @@ export default function AdminPage() {
|
||||
<AdminNavbar activeTab={activeTab} setActiveTab={setActiveTab} />
|
||||
|
||||
<main className="flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{activeTab === 'faucet' && <FaucetPanel />}
|
||||
{activeTab === 'wizard' && <MarketWizard />}
|
||||
{activeTab === 'governance' && <GovernancePanel />}
|
||||
{activeTab === 'adjudication' && <AdjudicationPanel />}
|
||||
{activeTab === 'faucet' && <FaucetPanel />}
|
||||
</main>
|
||||
|
||||
<footer className="border-t border-slate-900 py-6 text-center text-xs text-slate-600">
|
||||
WTFX Protocol Governance & Administration Console • Robinhood Chain Testnet (ID: 46630)
|
||||
WTFX Protocol SuperAdmin Console • Robinhood Chain Testnet (ID: 46630)
|
||||
</footer>
|
||||
</div>
|
||||
</Web3Provider>
|
||||
|
||||
@@ -12,10 +12,9 @@ export const AdminNavbar: React.FC<NavbarProps> = ({ activeTab, setActiveTab })
|
||||
const { account, isCorrectNetwork, connectWallet, switchNetwork } = useWeb3();
|
||||
|
||||
const navItems = [
|
||||
{ id: 'faucet', label: '🚰 WUSD 水龙头' },
|
||||
{ id: 'wizard', label: '🚀 创建市场向导' },
|
||||
{ id: 'governance', label: '⚙️ 协议参数治理' },
|
||||
{ id: 'adjudication', label: '⚖️ 市场风控与裁决' },
|
||||
{ id: 'governance', label: '⚙️ 协议全局治理' },
|
||||
{ id: 'adjudication', label: '⚖️ 市场争议终裁与熔断' },
|
||||
{ id: 'faucet', label: '🚰 超管铸币水龙头' },
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useWeb3 } from '../../context/Web3Context';
|
||||
import { DEPLOYMENTS } from '@wtfx/contracts';
|
||||
import { ethers } from 'ethers';
|
||||
import { Sliders, ShieldCheck, Percent, Wallet, Check, AlertCircle, Loader2, RefreshCw } from 'lucide-react';
|
||||
import { Sliders, ShieldCheck, Percent, Wallet, Check, AlertCircle, Loader2, RefreshCw, Layers, Award, Clock } from 'lucide-react';
|
||||
|
||||
export const GovernancePanel: React.FC = () => {
|
||||
const { account, signer, getControllerContract, isCorrectNetwork } = useWeb3();
|
||||
@@ -11,16 +11,20 @@ export const GovernancePanel: React.FC = () => {
|
||||
const [defaultFeeRate, setDefaultFeeRate] = useState<string>('0.6');
|
||||
const [creatorShare, setCreatorShare] = useState<string>('50');
|
||||
const [centralWallet, setCentralWallet] = useState<string>('');
|
||||
|
||||
// Tier 与 毕业阈值 & 争议窗口
|
||||
const [selectedTier, setSelectedTier] = useState<number>(1);
|
||||
const [tierStartAmount, setTierStartAmount] = useState<string>('1.1');
|
||||
const [tierPower, setTierPower] = useState<string>('1.0');
|
||||
const [graduationPoolCap, setGraduationPoolCap] = useState<string>('100000');
|
||||
const [graduationMaxSupply, setGraduationMaxSupply] = useState<string>('1000000');
|
||||
const [disputeWindowDays, setDisputeWindowDays] = useState<string>('1');
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [statusMsg, setStatusMsg] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
|
||||
|
||||
const fetchGovParams = async () => {
|
||||
try {
|
||||
const controller = getControllerContract();
|
||||
if (!controller) return;
|
||||
|
||||
// In real scenario, read from getters or view functions
|
||||
// We fill initialized defaults from deployment
|
||||
setTreasury(DEPLOYMENTS.robinhoodTestnet.governance.treasury);
|
||||
setCentralWallet(DEPLOYMENTS.robinhoodTestnet.governance.admin);
|
||||
} catch (err) {
|
||||
@@ -51,7 +55,7 @@ export const GovernancePanel: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateFeeRate = async () => {
|
||||
const handleUpdateTierConfig = async () => {
|
||||
if (!signer || !account) return;
|
||||
try {
|
||||
setLoading(true);
|
||||
@@ -59,11 +63,12 @@ export const GovernancePanel: React.FC = () => {
|
||||
const controller = getControllerContract();
|
||||
if (!controller) throw new Error('Controller not found');
|
||||
|
||||
const rateWei = ethers.parseUnits((Number(defaultFeeRate) / 100).toString(), 18);
|
||||
const tx = await controller.setFeeRateDefault(rateWei);
|
||||
setStatusMsg({ type: 'success', text: `更新默认手续费交易已广播: ${tx.hash.slice(0, 10)}...` });
|
||||
const startWei = ethers.parseUnits(tierStartAmount, 18);
|
||||
const powerWei = ethers.parseUnits(tierPower, 18);
|
||||
const tx = await controller.setMarketTier(selectedTier, startWei, powerWei);
|
||||
setStatusMsg({ type: 'success', text: `更新 Tier ${selectedTier} 曲线参数交易已广播: ${tx.hash.slice(0, 10)}...` });
|
||||
await tx.wait();
|
||||
setStatusMsg({ type: 'success', text: `🎉 全局默认协议手续费率已成功更新为: ${defaultFeeRate}%` });
|
||||
setStatusMsg({ type: 'success', text: `🎉 Tier ${selectedTier} 标准参数已成功更新 (Start: ${tierStartAmount}, Power: ${tierPower})!` });
|
||||
} catch (err: any) {
|
||||
setStatusMsg({ type: 'error', text: err.reason || err.message || '更新失败' });
|
||||
} finally {
|
||||
@@ -71,7 +76,7 @@ export const GovernancePanel: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateCreatorShare = async () => {
|
||||
const handleUpdateGraduation = async () => {
|
||||
if (!signer || !account) return;
|
||||
try {
|
||||
setLoading(true);
|
||||
@@ -79,11 +84,12 @@ export const GovernancePanel: React.FC = () => {
|
||||
const controller = getControllerContract();
|
||||
if (!controller) throw new Error('Controller not found');
|
||||
|
||||
const shareWei = ethers.parseUnits((Number(creatorShare) / 100).toString(), 18);
|
||||
const tx = await controller.setCreatorShare(shareWei);
|
||||
setStatusMsg({ type: 'success', text: `更新建盘者手续费分成交易已广播: ${tx.hash.slice(0, 10)}...` });
|
||||
const capWei = ethers.parseUnits(graduationPoolCap, 18);
|
||||
const supplyWei = ethers.parseUnits(graduationMaxSupply, 18);
|
||||
const tx = await controller.setGraduationThresholds(capWei, supplyWei);
|
||||
setStatusMsg({ type: 'success', text: `更新毕业资金池阈值交易已广播: ${tx.hash.slice(0, 10)}...` });
|
||||
await tx.wait();
|
||||
setStatusMsg({ type: 'success', text: `🎉 建盘者手续费返佣分成比例已成功更新为: ${creatorShare}%` });
|
||||
setStatusMsg({ type: 'success', text: `🎉 市场毕业门槛已更新:资金池达到 ${Number(graduationPoolCap).toLocaleString()} WUSD 自动毕业!` });
|
||||
} catch (err: any) {
|
||||
setStatusMsg({ type: 'error', text: err.reason || err.message || '更新失败' });
|
||||
} finally {
|
||||
@@ -91,7 +97,7 @@ export const GovernancePanel: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateCentralWallet = async () => {
|
||||
const handleUpdateDisputeWindow = async () => {
|
||||
if (!signer || !account) return;
|
||||
try {
|
||||
setLoading(true);
|
||||
@@ -99,10 +105,11 @@ export const GovernancePanel: React.FC = () => {
|
||||
const controller = getControllerContract();
|
||||
if (!controller) throw new Error('Controller not found');
|
||||
|
||||
const tx = await controller.setCentralWallet(centralWallet.trim());
|
||||
setStatusMsg({ type: 'success', text: `更新中央安全提币钱包交易已广播: ${tx.hash.slice(0, 10)}...` });
|
||||
const windowSec = Math.floor(Number(disputeWindowDays) * 86400);
|
||||
const tx = await controller.setDisputeWindow(windowSec);
|
||||
setStatusMsg({ type: 'success', text: `更新争议公示期窗口交易已广播: ${tx.hash.slice(0, 10)}...` });
|
||||
await tx.wait();
|
||||
setStatusMsg({ type: 'success', text: `🎉 中央安全提币钱包已成功更新为: ${centralWallet}` });
|
||||
setStatusMsg({ type: 'success', text: `🎉 市场初裁后的争议公示期已成功更新为: ${disputeWindowDays} 天!` });
|
||||
} catch (err: any) {
|
||||
setStatusMsg({ type: 'error', text: err.reason || err.message || '更新失败' });
|
||||
} finally {
|
||||
@@ -112,15 +119,15 @@ export const GovernancePanel: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto space-y-6">
|
||||
<div className="bg-slate-900 border border-slate-800 rounded-2xl p-6 shadow-xl space-y-6">
|
||||
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 shadow-xl space-y-6">
|
||||
<div className="border-b border-slate-800 pb-5 flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="p-3 bg-amber-500/10 rounded-xl border border-amber-500/20 text-amber-400">
|
||||
<div className="p-3 bg-amber-500/10 rounded-2xl border border-amber-500/20 text-amber-400">
|
||||
<Sliders className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-white">协议治理看板 (Protocol Governance)</h2>
|
||||
<p className="text-sm text-slate-400 mt-0.5">
|
||||
<h2 className="text-xl font-bold text-white">协议全局治理控制台 (SuperAdmin Protocol Governance)</h2>
|
||||
<p className="text-xs text-slate-400 mt-0.5">
|
||||
WTFControllerV2 智能合约超管参数配置(仅 DEFAULT_ADMIN_ROLE / OPERATOR_ROLE 可执行)
|
||||
</p>
|
||||
</div>
|
||||
@@ -128,100 +135,135 @@ export const GovernancePanel: React.FC = () => {
|
||||
|
||||
<button
|
||||
onClick={fetchGovParams}
|
||||
className="p-2 text-slate-400 hover:text-slate-200 hover:bg-slate-800 rounded-lg transition-colors"
|
||||
className="p-2 text-slate-400 hover:text-slate-200 hover:bg-slate-800 rounded-xl transition-colors"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Treasury Setting */}
|
||||
<div className="bg-slate-950/50 border border-slate-800 rounded-xl p-4 space-y-3">
|
||||
<div className="flex items-center space-x-2 text-sm font-semibold text-slate-200">
|
||||
<ShieldCheck className="w-4 h-4 text-cyan-400" />
|
||||
<span>协议国库金库 (Treasury Address)</span>
|
||||
{/* 1. Tier 治理档位配置 */}
|
||||
<div className="bg-slate-950/60 border border-slate-800 rounded-2xl p-5 space-y-4">
|
||||
<div className="flex items-center space-x-2 text-sm font-bold text-white">
|
||||
<Layers className="w-4 h-4 text-cyan-400" />
|
||||
<span>1. 市场治理分级参数 (Market Tier Curves Config)</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400">设置不同 Tier 市场启动时的 Power LDA 曲线底仓幂律参数。</p>
|
||||
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{[1, 2, 3].map((t) => (
|
||||
<button
|
||||
key={t}
|
||||
onClick={() => setSelectedTier(t)}
|
||||
className={`py-2 text-xs font-bold rounded-xl border transition-all ${
|
||||
selectedTier === t
|
||||
? 'bg-cyan-950/60 border-cyan-500 text-cyan-300'
|
||||
: 'bg-slate-900 border-slate-800 text-slate-400 hover:border-slate-700'
|
||||
}`}
|
||||
>
|
||||
Tier {t}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-slate-400 mb-1">底仓起点参数 (Start - 须 >= 1.1)</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.1"
|
||||
value={tierStartAmount}
|
||||
onChange={(e) => setTierStartAmount(e.target.value)}
|
||||
className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400">用于接收协议留存手续费分润的冷钱包/金库合约。</p>
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-slate-400 mb-1">曲率指数参数 (Power - 默认 1.0)</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.1"
|
||||
value={tierPower}
|
||||
onChange={(e) => setTierPower(e.target.value)}
|
||||
className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleUpdateTierConfig}
|
||||
disabled={loading || !account}
|
||||
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-xl text-xs font-bold transition-colors"
|
||||
>
|
||||
更新 Tier {selectedTier} 参数
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 2. 毕业阈值与争议公示期 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="bg-slate-950/60 border border-slate-800 rounded-2xl p-5 space-y-3">
|
||||
<div className="flex items-center space-x-2 text-sm font-bold text-white">
|
||||
<Award className="w-4 h-4 text-amber-400" />
|
||||
<span>2. 市场毕业门槛 (Graduation Pool Cap)</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400">当市场累积保证金池达到该阈值时自动毕业锁定。</p>
|
||||
<input
|
||||
type="number"
|
||||
value={graduationPoolCap}
|
||||
onChange={(e) => setGraduationPoolCap(e.target.value)}
|
||||
placeholder="100000"
|
||||
className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500"
|
||||
/>
|
||||
<button
|
||||
onClick={handleUpdateGraduation}
|
||||
disabled={loading || !account}
|
||||
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-xl text-xs font-bold transition-colors"
|
||||
>
|
||||
更新毕业资金池门槛
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-950/60 border border-slate-800 rounded-2xl p-5 space-y-3">
|
||||
<div className="flex items-center space-x-2 text-sm font-bold text-white">
|
||||
<Clock className="w-4 h-4 text-indigo-400" />
|
||||
<span>3. 争议公示期窗口 (Dispute Window)</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400">创建者初裁后允许社区提出异议的等待天数。</p>
|
||||
<input
|
||||
type="number"
|
||||
value={disputeWindowDays}
|
||||
onChange={(e) => setDisputeWindowDays(e.target.value)}
|
||||
placeholder="1"
|
||||
className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500"
|
||||
/>
|
||||
<button
|
||||
onClick={handleUpdateDisputeWindow}
|
||||
disabled={loading || !account}
|
||||
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-xl text-xs font-bold transition-colors"
|
||||
>
|
||||
更新争议窗口 (天)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 3. 国库与安全地址 */}
|
||||
<div className="bg-slate-950/60 border border-slate-800 rounded-2xl p-5 space-y-3">
|
||||
<div className="flex items-center space-x-2 text-sm font-bold text-white">
|
||||
<ShieldCheck className="w-4 h-4 text-emerald-400" />
|
||||
<span>4. 协议国库金库地址 (Treasury Address)</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400">接收协议留存 50% 手续费分润的冷钱包/金库合约。</p>
|
||||
<div className="flex space-x-2">
|
||||
<input
|
||||
type="text"
|
||||
value={treasury}
|
||||
onChange={(e) => setTreasury(e.target.value)}
|
||||
className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500"
|
||||
className="flex-1 bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500"
|
||||
/>
|
||||
<button
|
||||
onClick={handleUpdateTreasury}
|
||||
disabled={loading || !account}
|
||||
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-lg text-xs font-medium transition-colors"
|
||||
className="px-5 py-2 bg-cyan-600 hover:bg-cyan-500 text-white rounded-lg text-xs font-bold transition-colors disabled:opacity-50"
|
||||
>
|
||||
更新国库地址
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Central Wallet */}
|
||||
<div className="bg-slate-950/50 border border-slate-800 rounded-xl p-4 space-y-3">
|
||||
<div className="flex items-center space-x-2 text-sm font-semibold text-slate-200">
|
||||
<Wallet className="w-4 h-4 text-indigo-400" />
|
||||
<span>中央安全提币钱包 (Central Wallet)</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400">由超管授权以自动化划拨协议手续费的专用钱包。</p>
|
||||
<input
|
||||
type="text"
|
||||
value={centralWallet}
|
||||
onChange={(e) => setCentralWallet(e.target.value)}
|
||||
className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500"
|
||||
/>
|
||||
<button
|
||||
onClick={handleUpdateCentralWallet}
|
||||
disabled={loading || !account}
|
||||
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-lg text-xs font-medium transition-colors"
|
||||
>
|
||||
更新中央钱包
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Default Fee Rate */}
|
||||
<div className="bg-slate-950/50 border border-slate-800 rounded-xl p-4 space-y-3">
|
||||
<div className="flex items-center space-x-2 text-sm font-semibold text-slate-200">
|
||||
<Percent className="w-4 h-4 text-emerald-400" />
|
||||
<span>默认协议手续费率 (Fee Rate %)</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400">新建市场默认费率(范围 [0.1%, 3.0%],默认 0.6%)。</p>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
value={defaultFeeRate}
|
||||
onChange={(e) => setDefaultFeeRate(e.target.value)}
|
||||
className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500"
|
||||
/>
|
||||
<button
|
||||
onClick={handleUpdateFeeRate}
|
||||
disabled={loading || !account}
|
||||
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-lg text-xs font-medium transition-colors"
|
||||
>
|
||||
更新默认手续费
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Creator Share */}
|
||||
<div className="bg-slate-950/50 border border-slate-800 rounded-xl p-4 space-y-3">
|
||||
<div className="flex items-center space-x-2 text-sm font-semibold text-slate-200">
|
||||
<Percent className="w-4 h-4 text-pink-400" />
|
||||
<span>建盘者返佣分成比例 (Creator Share %)</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400">交易产生的手续费中分给市场创建者的比例(默认 50%)。</p>
|
||||
<input
|
||||
type="number"
|
||||
step="1"
|
||||
value={creatorShare}
|
||||
onChange={(e) => setCreatorShare(e.target.value)}
|
||||
className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-xs font-mono text-slate-200 focus:outline-none focus:border-cyan-500"
|
||||
/>
|
||||
<button
|
||||
onClick={handleUpdateCreatorShare}
|
||||
disabled={loading || !account}
|
||||
className="w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-600 rounded-lg text-xs font-medium transition-colors"
|
||||
>
|
||||
更新建盘者分成
|
||||
更新国库
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -229,16 +271,16 @@ export const GovernancePanel: React.FC = () => {
|
||||
{/* Status Message */}
|
||||
{statusMsg && (
|
||||
<div
|
||||
className={`p-4 rounded-xl border flex items-start space-x-3 text-sm ${
|
||||
className={`p-4 rounded-2xl border flex items-start space-x-3 text-xs ${
|
||||
statusMsg.type === 'success'
|
||||
? 'bg-emerald-950/30 border-emerald-800/50 text-emerald-300'
|
||||
: 'bg-rose-950/30 border-rose-800/50 text-rose-300'
|
||||
}`}
|
||||
>
|
||||
{statusMsg.type === 'success' ? (
|
||||
<Check className="w-5 h-5 flex-shrink-0 text-emerald-400" />
|
||||
<Check className="w-4 h-4 flex-shrink-0 text-emerald-400" />
|
||||
) : (
|
||||
<AlertCircle className="w-5 h-5 flex-shrink-0 text-rose-400" />
|
||||
<AlertCircle className="w-4 h-4 flex-shrink-0 text-rose-400" />
|
||||
)}
|
||||
<div className="flex-1 overflow-hidden break-words">
|
||||
<p>{statusMsg.text}</p>
|
||||
|
||||
Reference in New Issue
Block a user