aibanking.dev / views /CryptView.tsx
admin08077's picture
Upload 26 files
b8b3edf verified
import React, { useState, useEffect } from 'react';
import {
Hammer,
Sparkles,
ShieldCheck,
Terminal,
Wallet,
Key,
Loader2,
Eye,
Image as ImageIcon,
Zap,
Globe,
Share2,
ExternalLink,
Info,
AlertCircle,
// Added missing X icon import
X
} from 'lucide-react';
import { nftService, GeneratedNFT } from '../services/nftService.ts';
declare global {
interface Window {
ethereum?: any;
}
}
const CryptView: React.FC = () => {
const [prompt, setPrompt] = useState('');
const [isSynthesizing, setIsSynthesizing] = useState(false);
const [isMinting, setIsMinting] = useState(false);
const [nft, setNft] = useState<GeneratedNFT | null>(null);
const [openSeaKey, setOpenSeaKey] = useState('');
const [walletAddress, setWalletAddress] = useState('');
const [mintLogs, setMintLogs] = useState<string[]>([]);
const [walletConnected, setWalletConnected] = useState(false);
const [isConnecting, setIsConnecting] = useState(false);
const [error, setError] = useState<string | null>(null);
const connectWallet = async () => {
if (typeof window.ethereum === 'undefined') {
setError("MetaMask not detected. Please install the browser extension to interact with the foundry.");
return;
}
setIsConnecting(true);
setError(null);
try {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
if (accounts.length > 0) {
setWalletAddress(accounts[0]);
setWalletConnected(true);
}
} catch (err: any) {
console.error("Wallet Connection Failed:", err);
setError(err.message || "Failed to establish neural handshake with wallet.");
} finally {
setIsConnecting(false);
}
};
useEffect(() => {
const checkConnection = async () => {
if (window.ethereum) {
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
if (accounts.length > 0) {
setWalletAddress(accounts[0]);
setWalletConnected(true);
}
}
};
checkConnection();
if (window.ethereum) {
window.ethereum.on('accountsChanged', (accounts: string[]) => {
if (accounts.length > 0) {
setWalletAddress(accounts[0]);
setWalletConnected(true);
} else {
setWalletAddress('');
setWalletConnected(false);
}
});
}
}, []);
const handleSynthesize = async () => {
if (!prompt.trim()) return;
setIsSynthesizing(true);
setNft(null);
setMintLogs([]);
setError(null);
const [imageUrl, metadata] = await Promise.all([
nftService.generateImage(prompt),
nftService.generateMetadata(prompt)
]);
if (imageUrl) {
setNft({
name: metadata.name || "Neural Artifact",
description: metadata.description || "Synthesized by Lumina Oracle",
imageUrl,
traits: metadata.traits || []
});
} else {
setError("Asset synthesis failed. Please adjust your prompt parameters.");
}
setIsSynthesizing(false);
};
const handleMint = async () => {
if (!nft || !openSeaKey || !walletConnected) return;
setIsMinting(true);
setMintLogs(["Starting deployment sequence..."]);
const steps = await nftService.mintToOpenSea(nft, openSeaKey, walletAddress);
for (const step of steps) {
await new Promise(r => setTimeout(r, 800));
setMintLogs(prev => [...prev, step]);
}
setIsMinting(false);
};
const shortenAddress = (addr: string) => `${addr.substring(0, 6)}...${addr.substring(addr.length - 4)}`;
return (
<div className="space-y-10 animate-in fade-in duration-700 pb-20">
{/* Header */}
<div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
<div>
<h2 className="text-3xl font-black text-white italic tracking-tighter uppercase mb-2">Quantum <span className="text-blue-500 not-italic">Crypt</span></h2>
<p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">Neural Asset Synthesis & Blockchain Deployment</p>
</div>
<div className="flex gap-4">
<div className="px-6 py-3 bg-zinc-950 border border-zinc-800 rounded-2xl flex items-center gap-4 shadow-xl">
<div className="flex items-center gap-2">
<div className={`w-2 h-2 rounded-full ${walletConnected ? 'bg-emerald-500 animate-pulse' : 'bg-rose-500'}`}></div>
<span className="text-[10px] font-black text-zinc-500 uppercase tracking-widest">
Wallet: {walletConnected ? shortenAddress(walletAddress) : 'Disconnected'}
</span>
</div>
</div>
{!walletConnected && (
<button
onClick={connectWallet}
disabled={isConnecting}
className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all shadow-lg shadow-blue-900/30 disabled:opacity-50"
>
{isConnecting ? <Loader2 size={14} className="animate-spin" /> : <Wallet size={14} />}
<span>{isConnecting ? 'Initializing...' : 'Connect MetaMask'}</span>
</button>
)}
</div>
</div>
{error && (
<div className="p-4 bg-rose-500/10 border border-rose-500/20 rounded-2xl flex items-center gap-4 animate-in slide-in-from-top-2">
<AlertCircle size={20} className="text-rose-500 shrink-0" />
<p className="text-xs font-bold text-rose-200">{error}</p>
<button onClick={() => setError(null)} className="ml-auto p-1 hover:bg-rose-500/10 rounded-lg text-rose-500">
<X size={16} />
</button>
</div>
)}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-10">
{/* Left: Configuration & Foundry */}
<div className="lg:col-span-1 space-y-8">
<div className="bg-zinc-950 border border-zinc-900 rounded-[2.5rem] p-8 shadow-2xl space-y-8">
<div className="flex items-center gap-3 mb-2">
<div className="p-3 bg-blue-600/10 text-blue-500 rounded-xl">
<Key size={18} />
</div>
<h3 className="text-white font-black text-xs uppercase tracking-widest italic">Integration Layer</h3>
</div>
<div className="space-y-6">
<div className="space-y-2">
<label className="text-[9px] font-black text-zinc-600 uppercase tracking-widest ml-1">OpenSea API Key</label>
<input
type="password"
value={openSeaKey}
onChange={e => setOpenSeaKey(e.target.value)}
placeholder="X-API-KEY-..."
className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-xl py-3 px-4 text-white text-xs outline-none transition-all placeholder:text-zinc-800 font-mono"
/>
</div>
<div className="space-y-2">
<label className="text-[9px] font-black text-zinc-600 uppercase tracking-widest ml-1">Synthesis Prompt</label>
<textarea
value={prompt}
onChange={e => setPrompt(e.target.value)}
placeholder="e.g. Cybernetic obsidian eagle with neon circuits..."
className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-xl py-4 px-4 text-white text-xs outline-none transition-all placeholder:text-zinc-800 font-bold min-h-[120px] resize-none"
/>
</div>
<button
onClick={handleSynthesize}
disabled={isSynthesizing || !prompt}
className="w-full py-4 bg-zinc-900 hover:bg-zinc-100 hover:text-black text-white rounded-2xl font-black text-[10px] uppercase tracking-[0.2em] transition-all border border-zinc-800 flex items-center justify-center gap-3 disabled:opacity-50"
>
{isSynthesizing ? <Loader2 className="animate-spin" size={16} /> : <Sparkles size={16} />}
<span>{isSynthesizing ? 'Synthesizing Neural Path...' : 'Synthesize Artifact'}</span>
</button>
</div>
</div>
{/* Minting Log Terminal */}
<div className="bg-black border border-zinc-900 rounded-[2.5rem] overflow-hidden shadow-2xl h-[300px] flex flex-col">
<div className="bg-zinc-900/80 px-6 py-3 border-b border-zinc-800 flex justify-between items-center">
<div className="flex items-center gap-2">
<Terminal size={12} className="text-emerald-500" />
<span className="text-[9px] font-black uppercase tracking-widest text-zinc-500">Chain Trace Log</span>
</div>
<div className="flex gap-1.5">
<div className="w-1.5 h-1.5 rounded-full bg-zinc-800"></div>
<div className="w-1.5 h-1.5 rounded-full bg-zinc-800"></div>
<div className="w-1.5 h-1.5 rounded-full bg-zinc-800"></div>
</div>
</div>
<div className="flex-1 p-6 font-mono text-[10px] overflow-y-auto custom-scrollbar space-y-1.5">
{mintLogs.length === 0 ? (
<div className="text-zinc-700 italic">Waiting for forge command...</div>
) : (
mintLogs.map((log, i) => (
<div key={i} className={`${log.includes('Successfully') ? 'text-emerald-500' : 'text-zinc-500'}`}>
<span className="text-zinc-800 mr-2">[{new Date().toLocaleTimeString([], { hour12: false })}]</span>
{log}
</div>
))
)}
{isMinting && <div className="text-blue-500 animate-pulse font-black">Executing Quantum Block...</div>}
</div>
</div>
</div>
{/* Center/Right: Preview & Metadata */}
<div className="lg:col-span-2 space-y-8">
<div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-10 shadow-2xl relative overflow-hidden group">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_0%,_#1e1b4b_0%,_transparent_50%)] opacity-30"></div>
{!nft && !isSynthesizing ? (
<div className="h-[500px] flex flex-col items-center justify-center text-center space-y-6 relative z-10 border-2 border-dashed border-zinc-900 rounded-[2rem]">
<div className="w-20 h-20 bg-zinc-900 rounded-full flex items-center justify-center text-zinc-700">
<ImageIcon size={40} />
</div>
<div>
<h3 className="text-zinc-600 font-black uppercase tracking-[0.4em] text-sm">Foundry Idle</h3>
<p className="text-zinc-700 text-xs mt-2 font-medium">Input parameters to initialize synthesis.</p>
</div>
</div>
) : isSynthesizing ? (
<div className="h-[500px] flex flex-col items-center justify-center text-center space-y-8 relative z-10">
<div className="relative">
<div className="w-48 h-48 rounded-full border-4 border-blue-500/20 border-t-blue-500 animate-spin"></div>
<div className="absolute inset-0 flex items-center justify-center">
<Zap size={48} className="text-blue-500 animate-pulse" />
</div>
</div>
<div className="space-y-2">
<h3 className="text-white font-black uppercase tracking-[0.4em] italic animate-pulse">Forging Neural Layers</h3>
<p className="text-zinc-500 text-[10px] font-black uppercase tracking-widest">Estimated completion: 8.2s</p>
</div>
</div>
) : nft ? (
<div className="flex flex-col lg:flex-row gap-12 relative z-10 animate-in zoom-in-95 duration-700">
<div className="lg:w-1/2">
<div className="relative group">
<div className="absolute -inset-1 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-[2.5rem] blur opacity-25 group-hover:opacity-75 transition duration-1000 group-hover:duration-200"></div>
<img
src={nft.imageUrl}
className="relative w-full aspect-square rounded-[2rem] bg-black border border-zinc-800 object-cover shadow-2xl"
alt="Synthesized NFT"
/>
<div className="absolute top-4 right-4 flex gap-2">
<div className="bg-black/60 backdrop-blur-md px-3 py-1.5 rounded-full border border-white/10 flex items-center gap-2">
<ShieldCheck size={12} className="text-emerald-500" />
<span className="text-[8px] font-black text-white uppercase tracking-widest">AI Verified</span>
</div>
</div>
</div>
</div>
<div className="lg:w-1/2 flex flex-col justify-between">
<div className="space-y-8">
<div>
<span className="px-3 py-1 bg-blue-600/10 text-blue-500 border border-blue-500/20 rounded-full text-[9px] font-black uppercase tracking-widest mb-4 inline-block">Unminted Asset</span>
<h3 className="text-4xl font-black text-white italic tracking-tighter uppercase mb-2 leading-none">{nft.name}</h3>
<p className="text-zinc-500 text-sm leading-relaxed font-medium italic">"{nft.description}"</p>
</div>
<div className="grid grid-cols-2 gap-4">
{nft.traits.map((trait, i) => (
<div key={i} className="p-4 bg-black/40 border border-zinc-800 rounded-2xl">
<p className="text-[8px] font-black text-zinc-600 uppercase tracking-widest mb-1">{trait.trait_type}</p>
<p className="text-white font-bold text-xs uppercase">{trait.value}</p>
</div>
))}
</div>
</div>
<div className="pt-10 flex gap-4">
<button
onClick={handleMint}
disabled={isMinting || !openSeaKey || !walletConnected}
className="flex-1 py-5 bg-blue-600 hover:bg-blue-500 text-white rounded-[2rem] font-black text-xs uppercase tracking-[0.3em] transition-all flex items-center justify-center gap-4 shadow-xl shadow-blue-900/30 disabled:opacity-50"
>
{isMinting ? <Loader2 className="animate-spin" size={20} /> : <Hammer size={20} />}
<span>{isMinting ? 'Casting Block...' : 'Forge to OpenSea'}</span>
</button>
<button className="p-5 bg-zinc-900 hover:bg-zinc-800 text-zinc-500 hover:text-white rounded-[2rem] transition-all border border-zinc-800">
<Share2 size={20} />
</button>
</div>
</div>
</div>
) : null}
</div>
<div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-10 relative overflow-hidden group shadow-2xl">
<div className="flex flex-col md:flex-row justify-between items-center gap-10">
<div className="flex items-center gap-8 flex-1 w-full">
<div className="w-20 h-20 bg-emerald-500/10 rounded-3xl flex items-center justify-center text-emerald-500 shadow-2xl shadow-emerald-500/5">
<ShieldCheck size={40} />
</div>
<div className="flex-1">
<h4 className="text-xl font-black text-white italic tracking-tighter uppercase mb-1">OpenSea <span className="text-emerald-500 not-italic">Verified Foundry</span></h4>
<p className="text-zinc-500 text-xs font-medium max-w-md">Lumina Quantum Foundry utilizes dedicated node architecture to ensure gas-optimized minting and instant marketplace visibility.</p>
</div>
</div>
<button className="flex items-center gap-2 text-zinc-600 hover:text-white transition-colors">
<span className="text-[10px] font-black uppercase tracking-widest">Review Protocol</span>
<ExternalLink size={14} />
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default CryptView;