feat(web): add network subpages and subdomains listing page

- Add /subdomains page listing all 20 PezkuwiChain subdomains
- Add Back to Home button to Subdomains page
- Create NetworkPage reusable component for network details
- Add 7 network subpages: /mainnet, /staging, /testnet, /beta, /alfa, /development, /local
- Update ChainSpecs network cards to navigate to network subpages
- Add i18n translations for chainSpecs section in en.ts
- Add SDK docs with rebranding support (rebrand-rustdoc.cjs)
- Add generate-docs-structure.cjs for automatic docs generation
- Update shared libs: endpoints, polkadot, wallet, xcm-bridge
- Add new token logos: TYR, ZGR, pezkuwi_icon
- Add new pages: Explorer, Docs, Wallet, Api, Faucet, Developers, Grants, Wiki, Forum, Telemetry
This commit is contained in:
2025-12-11 00:33:47 +03:00
parent 2c6c4f5606
commit 11678fe7cd
976 changed files with 60601 additions and 168 deletions
+97
View File
@@ -0,0 +1,97 @@
import React from 'react';
import Layout from '@/components/Layout';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
const CodeSnippet = ({ language, code }: { language: string, code: string }) => (
<SyntaxHighlighter language={language} style={vscDarkPlus} customStyle={{ margin: 0, padding: '1rem', backgroundColor: '#1E1E1E', borderRadius: '0.5rem' }}>
{code}
</SyntaxHighlighter>
);
const Api: React.FC = () => {
const getBlockResponse = `{
"blockNumber": 123456,
"hash": "0xabcde12345fghij67890klmno12345pqrst67890uvwxyz12345abcde12345",
"parentHash": "0x12345abcde12345fghij67890klmno12345pqrst67890uvwxyz12345abc",
"timestamp": "2025-12-10T10:30:00Z",
"transactions": [
"0x98765fedcba..."
]
}`;
const getTxResponse = `{
"txHash": "0x98765fedcba...",
"blockNumber": 123456,
"from": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"to": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
"amount": "100.00 HEZ",
"fee": "0.01 HEZ"
}`;
return (
<Layout>
<div className="container mx-auto px-4 py-8 text-white">
<h1 className="text-4xl font-bold mb-8">API Documentation</h1>
<div className="space-y-12">
<div>
<h2 className="text-2xl font-bold mb-4 border-b-2 border-gray-700 pb-2">Endpoints</h2>
<div className="space-y-4">
<div className="flex items-center">
<span className="text-sm font-bold bg-green-600 text-white rounded px-2 py-1 mr-4">GET</span>
<span className="font-mono text-lg">/api/blocks/latest</span>
</div>
<div className="flex items-center">
<span className="text-sm font-bold bg-green-600 text-white rounded px-2 py-1 mr-4">GET</span>
<span className="font-mono text-lg">/api/blocks/{'{blockNumber}'}</span>
</div>
<div className="flex items-center">
<span className="text-sm font-bold bg-green-600 text-white rounded px-2 py-1 mr-4">GET</span>
<span className="font-mono text-lg">/api/transactions/{'{txHash}'}</span>
</div>
</div>
</div>
<div className="space-y-8">
<h2 className="text-2xl font-bold mb-4 border-b-2 border-gray-700 pb-2">Examples</h2>
{/* Get Block Example */}
<div>
<h3 className="text-xl font-semibold mb-2 font-mono">GET /api/blocks/{'{blockNumber}'}</h3>
<p className="text-gray-400 mb-4">Retrieve a specific block by its number.</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<h4 className="font-bold mb-2 text-gray-300">Request</h4>
<CodeSnippet language="bash" code={'curl https://api.pezkuwichain.io/api/blocks/123456'} />
</div>
<div>
<h4 className="font-bold mb-2 text-gray-300">Response</h4>
<CodeSnippet language="json" code={getBlockResponse} />
</div>
</div>
</div>
{/* Get Transaction Example */}
<div>
<h3 className="text-xl font-semibold mb-2 font-mono">GET /api/transactions/{'{txHash}'}</h3>
<p className="text-gray-400 mb-4">Retrieve a specific transaction by its hash.</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<h4 className="font-bold mb-2 text-gray-300">Request</h4>
<CodeSnippet language="bash" code={'curl https://api.pezkuwichain.io/api/transactions/0x98765...'} />
</div>
<div>
<h4 className="font-bold mb-2 text-gray-300">Response</h4>
<CodeSnippet language="json" code={getTxResponse} />
</div>
</div>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default Api;
+115
View File
@@ -0,0 +1,115 @@
import React from 'react';
import Layout from '@/components/Layout';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { Download, Book, MessageCircle, Github } from 'lucide-react';
const CodeSnippet = ({ language, code }: { language: string, code: string }) => (
<SyntaxHighlighter language={language} style={vscDarkPlus} customStyle={{ margin: 0, padding: '1rem', backgroundColor: '#1E1E1E', borderRadius: '0.5rem' }}>
{code}
</SyntaxHighlighter>
);
const Developers: React.FC = () => {
const connectCode = `import { ApiPromise, WsProvider } from '@polkadot/api';
// Connect to the PezkuwiChain node
const wsProvider = new WsProvider('wss://rpc.pezkuwichain.io');
const api = await ApiPromise.create({ provider: wsProvider });
console.log('API is connected:', api.isConnected);`;
const transferCode = `// Example: Transfer HEZ tokens
const keyring = new Keyring({ type: 'sr25519' });
const alice = keyring.addFromUri('//Alice');
const bob = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty';
const unsub = await api.tx.balances
.transfer(bob, 12345)
.signAndSend(alice, (result) => {
console.log(\`Current status is \${result.status}\`);
if (result.isFinalized) {
console.log(\`Transaction finalized at blockHash \${result.status.asFinalized}\`);
unsub();
}
});`;
return (
<Layout>
<div className="container mx-auto px-4 py-8 text-white">
<div className="text-center mb-12">
<h1 className="text-5xl font-bold mb-2">Developer Portal</h1>
<p className="text-xl text-gray-400">Everything you need to build on PezkuwiChain.</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{/* Quick Start Guide */}
<div className="bg-gray-800 p-6 rounded-lg">
<Book className="text-blue-400 mb-4" size={32} />
<h2 className="text-2xl font-bold mb-2">Quick Start Guide</h2>
<p className="text-gray-400 mb-4">Your first steps to building a dApp on PezkuwiChain.</p>
<ol className="list-decimal list-inside space-y-2">
<li>Install the Polkadot.js extension.</li>
<li>Get some testnet HEZ from the Faucet.</li> <li>Clone a starter project from our GitHub.</li>
<li>Start building!</li>
</ol>
</div>
{/* SDKs */}
<div className="bg-gray-800 p-6 rounded-lg">
<Download className="text-green-400 mb-4" size={32} />
<h2 className="text-2xl font-bold mb-2">SDK Downloads</h2>
<p className="text-gray-400 mb-4">Libraries to interact with the chain.</p>
<div className="space-y-3">
<a href="#" className="flex items-center text-blue-400 hover:text-white">
<Github className="mr-2" size={20} />
<span>Javascript/Typescript SDK</span>
</a>
<a href="#" className="flex items-center text-blue-400 hover:text-white">
<Github className="mr-2" size={20} />
<span>Rust SDK</span>
</a>
<a href="#" className="flex items-center text-blue-400 hover:text-white">
<Github className="mr-2" size={20} />
<span>Python SDK</span>
</a>
</div>
</div>
{/* Community */}
<div className="bg-gray-800 p-6 rounded-lg">
<MessageCircle className="text-purple-400 mb-4" size={32} />
<h2 className="text-2xl font-bold mb-2">Community</h2>
<p className="text-gray-400 mb-4">Get help and connect with other developers.</p>
<div className="space-y-3">
<a href="#" className="flex items-center text-blue-400 hover:text-white">
<Github className="mr-2" size={20} />
<span>GitHub</span>
</a>
<a href="#" className="flex items-center text-blue-400 hover:text-white">
<MessageCircle className="mr-2" size={20} />
<span>Discord</span>
</a>
</div>
</div>
</div>
<div className="mt-12">
<h2 className="text-3xl font-bold text-center mb-8">Code Examples</h2>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div>
<h3 className="text-xl font-semibold mb-2">Connect to the Network</h3>
<CodeSnippet language="javascript" code={connectCode} />
</div>
<div>
<h3 className="text-xl font-semibold mb-2">Make a Transfer</h3>
<CodeSnippet language="javascript" code={transferCode} />
</div>
</div>
</div>
</div>
</Layout>
);
};
export default Developers;
+309
View File
@@ -0,0 +1,309 @@
import React, { useState, useEffect, useMemo } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import Layout from '@/components/Layout';
import { marked } from 'marked';
import { ChevronRight, Book, ExternalLink } from 'lucide-react';
import DOMPurify from 'dompurify';
// SDK Embedded View - shown inline in the content area (window in window style)
const SDKEmbeddedView: React.FC = () => {
const sdkUrl = '/sdk_docs/pezkuwi_sdk_docs/index.html';
return (
<div className="flex flex-col h-full min-h-[600px]">
{/* SDK Panel Header */}
<div className="flex items-center gap-3 px-4 py-3 bg-gray-800 rounded-t-lg border border-gray-700 border-b-0">
<img
src="/pezkuwi_icon.png"
alt="Pezkuwi"
className="w-8 h-8 rounded"
/>
<div>
<h3 className="text-white font-semibold">pezkuwi_sdk_docs</h3>
<span className="text-gray-400 text-xs">0.0.1</span>
</div>
<div className="ml-auto flex items-center gap-2">
<a
href={sdkUrl}
target="_blank"
rel="noopener noreferrer"
className="p-2 hover:bg-gray-700 rounded-md transition-colors text-gray-400 hover:text-white"
title="Open in new tab"
>
<ExternalLink size={16} />
</a>
</div>
</div>
{/* SDK Docs iframe */}
<div className="flex-1 border border-gray-700 rounded-b-lg overflow-hidden bg-white">
<iframe
src={sdkUrl}
title="Pezkuwi SDK Documentation"
className="w-full h-full border-0"
style={{ minHeight: '550px' }}
/>
</div>
</div>
);
};
const SidebarNav: React.FC<{ structure: object, onLinkClick: () => void, onSDKClick: () => void }> = ({ structure, onLinkClick, onSDKClick }) => {
const [openCategories, setOpenCategories] = useState<string[]>(['Getting Started', 'SDK Reference', 'General Docs', 'Contributor Guide']);
const toggleCategory = (category: string) => {
setOpenCategories(prev =>
prev.includes(category)
? prev.filter(c => c !== category)
: [...prev, category]
);
};
const renderNav = (struct: any) => {
return Object.entries(struct).map(([key, value]) => {
if (typeof value === 'string') {
// Check if it's the SDK docs special link
const isSDKLink = value === 'sdk://open';
if (isSDKLink) {
return (
<li key={key}>
<button
onClick={() => {
onSDKClick();
}}
className="w-full text-left block py-1 px-2 rounded-md hover:bg-gray-700 transition-colors font-bold text-green-400 hover:text-green-300 flex items-center gap-2"
>
{key}
</button>
</li>
);
}
const path = value.replace(/\.(md|rs)$/, '');
return (
<li key={path}>
<Link
to={`/docs/${path}`}
onClick={onLinkClick}
className="block py-1 px-2 rounded-md hover:bg-gray-700 transition-colors text-gray-300 hover:text-white"
>
{key}
</Link>
</li>
);
} else {
const isExpanded = openCategories.includes(key);
return (
<li key={key}>
<div
onClick={() => toggleCategory(key)}
className="flex justify-between items-center cursor-pointer py-2 px-2 rounded-md hover:bg-gray-700"
>
<span className="font-semibold text-white">{key}</span>
<ChevronRight size={16} className={`transform transition-transform text-gray-400 ${isExpanded ? 'rotate-90' : ''}`} />
</div>
{isExpanded && (
<ul className="pl-4 border-l border-gray-600 ml-2">
{renderNav(value)}
</ul>
)}
</li>
);
}
});
};
return <nav><ul className="space-y-1">{renderNav(structure)}</ul></nav>;
};
const Docs: React.FC = () => {
const { '*': splat } = useParams();
const navigate = useNavigate();
const [docStructure, setDocStructure] = useState<object | null>(null);
const [content, setContent] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [showSDKLanding, setShowSDKLanding] = useState(false);
// Fetch the documentation structure
useEffect(() => {
fetch('/docs-structure.json')
.then(res => {
if (!res.ok) {
throw new Error('Failed to load documentation structure.');
}
return res.json();
})
.then(data => setDocStructure(data))
.catch(e => setError(e.message));
}, []);
const filePath = useMemo(() => {
// If no splat, and the structure is loaded, default to the introduction markdown
if (!splat && docStructure) {
const defaultEntry = docStructure['Introduction'];
if (typeof defaultEntry === 'string') {
return defaultEntry;
}
} else if (splat) {
// Check if it's an SDK link which is an HTML file
if (splat.startsWith('sdk_docs/') && splat.endsWith('html')) {
return splat; // Treat as direct path, no .md or .rs append
}
return `${splat}.md`; // For .md or .rs files
}
return null; // No file selected, no default provided yet
}, [splat, docStructure]);
// If no splat and no default, avoid fetching content
const shouldFetchContent = !!filePath && !filePath.startsWith('sdk_docs/'); // Do not fetch content if it's an external SDK link
useEffect(() => {
if (!shouldFetchContent) {
setContent(''); // Clear content if not fetching
setError(null);
setIsLoading(false);
return;
}
const fetchContent = async () => {
setIsLoading(true);
setError(null);
try {
const response = await fetch(`/docs/${filePath}`);
if (!response.ok) {
throw new Error(`Documentation file not found: ${filePath}`);
}
let text = await response.text();
// If the file is a Rust file, wrap it in a markdown code block
if (filePath.endsWith('.rs')) {
text = '```rust\n' + text + '\n```';
}
const renderer = new marked.Renderer();
renderer.image = (href, title, text) => {
try {
// The base URL for the markdown file itself
const base = new URL(`/docs/${filePath}`, window.location.origin);
// Resolve the image's relative path against the markdown file's path
const imageUrl = new URL(href, base);
// Return the final path part of the URL
return `<img src="${imageUrl.pathname}" alt="${text}" title="${title || ''}" />`;
} catch (e) {
console.error("Error processing image URL:", e);
// Fallback to the original href if URL construction fails
return `<img src="${href}" alt="${text}" title="${title || ''}" />`;
}
};
marked.setOptions({ renderer });
const parsed = await marked.parse(text);
const sanitized = DOMPurify.sanitize(parsed);
setContent(sanitized);
} catch (e: any) {
setError(e.message);
setContent('');
} finally {
setIsLoading(false);
}
};
fetchContent();
}, [filePath, shouldFetchContent]); // Dependency array
// Check if we're on SDK route
const isSDKRoute = splat === 'sdk';
return (
<Layout>
<div className="flex h-full overflow-hidden">
{/* Sidebar */}
<aside
className={`fixed lg:static top-16 left-0 h-full lg:h-auto z-30 w-64 bg-gray-800 lg:bg-transparent lg:w-1/4 lg:pr-8 py-4 transition-transform transform ${isSidebarOpen ? 'translate-x-0' : '-translate-x-full'} lg:translate-x-0`}
>
<div className="px-4">
{docStructure ? (
<SidebarNav
structure={docStructure}
onLinkClick={() => {
setIsSidebarOpen(false);
setShowSDKLanding(false); // Clear SDK landing when navigating to other docs
}}
onSDKClick={() => {
setIsSidebarOpen(false);
setShowSDKLanding(true); // Show SDK landing
setContent(''); // Clear any markdown content
navigate('/docs/sdk');
}}
/>
) : (
<p className="text-gray-400">Loading navigation...</p>
)}
</div>
</aside>
{/* Mobile Sidebar Toggle */}
<button
className="fixed bottom-4 right-4 lg:hidden w-12 h-12 bg-green-600 rounded-full z-40 flex items-center justify-center text-white shadow-lg"
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
>
<Book size={24} />
</button>
{/* Main Content */}
<main className="w-full lg:w-3/4 lg:pl-8 flex flex-col">
<div className="prose prose-invert prose-headings:text-cyan-400 prose-a:text-blue-400 hover:prose-a:text-blue-300 prose-code:text-yellow-400 prose-pre:bg-gray-800 prose-pre:p-4 prose-pre:rounded-md max-w-none flex-1 min-h-0">
{isLoading && <p className="text-gray-400">Loading...</p>}
{error && <p className="text-red-400">Error: {error}</p>}
{/* SDK Embedded View - window in window style */}
{(showSDKLanding || isSDKRoute) && (
<SDKEmbeddedView />
)}
{/* Regular Markdown Content */}
{!isLoading && !error && content && !showSDKLanding && !isSDKRoute && (
<div dangerouslySetInnerHTML={{ __html: content }} />
)}
{/* Default Welcome */}
{!isLoading && !error && !content && !splat && !showSDKLanding && (
<div className="text-center py-12">
<div className="mb-8">
<div className="text-6xl mb-4">📖</div>
<h1 className="text-3xl font-bold text-white mb-2">PezkuwiChain Documentation</h1>
<p className="text-lg text-gray-400">Learn how to build on PezkuwiChain</p>
</div>
<p className="text-xl text-gray-400 mb-4">
Select a document from the sidebar to get started.
</p>
<div className="flex flex-wrap gap-4 justify-center mt-8">
<Link to="/docs/GENESIS_ENGINEERING_PLAN" className="px-4 py-2 bg-green-600 hover:bg-green-500 text-white rounded-lg transition-colors">
📋 Introduction
</Link>
<Link
to="/docs/sdk"
onClick={() => setShowSDKLanding(true)}
className="px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition-colors"
>
📚 SDK Docs
</Link>
<Link to="/docs/whitepaper/whitepaper" className="px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition-colors">
📄 Whitepaper
</Link>
</div>
</div>
)}
</div>
</main>
</div>
</Layout>
);
};
export default Docs;
+108
View File
@@ -0,0 +1,108 @@
import React from 'react';
import Layout from '@/components/Layout';
const Explorer: React.FC = () => {
return (
<Layout>
<div className="container mx-auto px-4 py-8 text-white">
<div className="flex flex-col md:flex-row justify-between items-center mb-8">
<h1 className="text-4xl font-bold text-blue-400">Block Explorer</h1>
<div className="w-full md:w-1/2 mt-4 md:mt-0">
<input
type="text"
placeholder="Search by Block / Tx / Address"
className="w-full px-4 py-2 rounded-lg bg-gray-800 text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div className="bg-gray-800 p-4 rounded-lg">
<h3 className="text-lg font-semibold text-gray-400">Total Blocks</h3>
<p className="text-2xl font-bold">123,456</p>
</div>
<div className="bg-gray-800 p-4 rounded-lg">
<h3 className="text-lg font-semibold text-gray-400">Transactions per Second (TPS)</h3>
<p className="text-2xl font-bold">15</p>
</div>
<div className="bg-gray-800 p-4 rounded-lg">
<h3 className="text-lg font-semibold text-gray-400">Active Validators</h3>
<p className="text-2xl font-bold">42</p>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div>
<h2 className="text-2xl font-bold mb-4">Latest Blocks</h2>
<div className="space-y-4">
<div className="bg-gray-800 p-4 rounded-lg">
<div className="flex justify-between">
<span className="font-bold text-blue-400">Block #123456</span>
<span className="text-gray-400">10 secs ago</span>
</div>
<div className="text-sm">
<span>Includes <span className="font-semibold text-green-400">5</span> transactions</span>
</div>
</div>
<div className="bg-gray-800 p-4 rounded-lg">
<div className="flex justify-between">
<span className="font-bold text-blue-400">Block #123455</span>
<span className="text-gray-400">25 secs ago</span>
</div>
<div className="text-sm">
<span>Includes <span className="font-semibold text-green-400">12</span> transactions</span>
</div>
</div>
<div className="bg-gray-800 p-4 rounded-lg">
<div className="flex justify-between">
<span className="font-bold text-blue-400">Block #123454</span>
<span className="text-gray-400">45 secs ago</span>
</div>
<div className="text-sm">
<span>Includes <span className="font-semibold text-green-400">8</span> transactions</span>
</div>
</div>
</div>
</div>
<div>
<h2 className="text-2xl font-bold mb-4">Latest Transactions</h2>
<div className="space-y-4">
<div className="bg-gray-800 p-4 rounded-lg">
<div className="flex justify-between">
<span className="font-mono text-sm text-purple-400 truncate">0xabcdef123...</span>
<span className="text-gray-400">12 secs ago</span>
</div>
<div className="text-sm">
<span className="font-semibold">From:</span> <span className="font-mono text-xs text-gray-300">5Grwva...</span>
<span className="font-semibold ml-2">To:</span> <span className="font-mono text-xs text-gray-300">5FHne...</span>
</div>
</div>
<div className="bg-gray-800 p-4 rounded-lg">
<div className="flex justify-between">
<span className="font-mono text-sm text-purple-400 truncate">0x123456abc...</span>
<span className="text-gray-400">30 secs ago</span>
</div>
<div className="text-sm">
<span className="font-semibold">From:</span> <span className="font-mono text-xs text-gray-300">5DAAn...</span>
<span className="font-semibold ml-2">To:</span> <span className="font-mono text-xs text-gray-300">5G6s6...</span>
</div>
</div>
<div className="bg-gray-800 p-4 rounded-lg">
<div className="flex justify-between">
<span className="font-mono text-sm text-purple-400 truncate">0x7890ab123...</span>
<span className="text-gray-400">50 secs ago</span>
</div>
<div className="text-sm">
<span className="font-semibold">From:</span> <span className="font-mono text-xs text-gray-300">5Hp2d...</span>
<span className="font-semibold ml-2">To:</span> <span className="font-mono text-xs text-gray-300">5E5s3...</span>
</div>
</div>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default Explorer;
+104
View File
@@ -0,0 +1,104 @@
import React, { useState, useEffect } from 'react';
import Layout from '@/components/Layout';
import { Clock, Send } from 'lucide-react';
const Faucet: React.FC = () => {
const [address, setAddress] = useState('');
const [cooldown, setCooldown] = useState(0);
const [token, setToken] = useState('HEZ');
useEffect(() => {
let timer: NodeJS.Timeout;
if (cooldown > 0) {
timer = setTimeout(() => setCooldown(cooldown - 1), 1000);
}
return () => clearTimeout(timer);
}, [cooldown]);
const handleRequest = () => {
// Mock request logic
if (address) {
console.log(`Requesting ${token} for address:`, address);
setCooldown(300); // 5 minutes cooldown
}
};
const formatTime = (seconds: number) => {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes}:${remainingSeconds < 10 ? '0' : ''}${remainingSeconds}`;
};
const recentDistributions = [
{ token: 'HEZ', address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', txHash: '0xabcde...', time: '2 minutes ago' },
{ token: 'PEZ', address: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', txHash: '0x12345...', time: '5 minutes ago' },
{ token: 'wHEZ', address: '5DAAn...c1s', txHash: '0x789ab...', time: '15 minutes ago' },
];
return (
<Layout>
<div className="container mx-auto px-4 py-8 text-white flex flex-col items-center">
<div className="w-full max-w-2xl text-center">
<h1 className="text-4xl font-bold mb-2 text-yellow-400">Testnet Faucet</h1>
<p className="text-gray-400 mb-8">Get testnet tokens to build and test your dApps on PezkuwiChain.</p>
<div className="bg-gray-800 p-8 rounded-lg shadow-lg">
<div className="flex mb-4">
<select
value={token}
onChange={(e) => setToken(e.target.value)}
className="p-3 rounded-l-lg bg-gray-700 text-white focus:outline-none"
>
<option>HEZ</option>
<option>PEZ</option>
<option>wHEZ</option>
</select>
<input
type="text"
placeholder="Enter your wallet address"
value={address}
onChange={(e) => setAddress(e.target.value)}
className="w-full p-3 bg-gray-700 text-white focus:outline-none rounded-r-lg"
/>
</div>
<button
onClick={handleRequest}
disabled={cooldown > 0 || !address}
className="w-full bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-3 px-4 rounded-lg transition-colors disabled:bg-gray-600 disabled:cursor-not-allowed flex items-center justify-center"
>
{cooldown > 0 ? (
<>
<Clock className="mr-2" size={20} />
<span>Wait for {formatTime(cooldown)}</span>
</>
) : (
<>
<Send className="mr-2" size={20} />
<span>Request Tokens</span>
</>
)}
</button>
</div>
<div className="mt-12 w-full">
<h2 className="text-2xl font-bold mb-4 text-left">Recent Distributions</h2>
<div className="space-y-3">
{recentDistributions.map((dist, index) => (
<div key={index} className="bg-gray-800 p-3 rounded-lg flex justify-between items-center text-sm">
<div className="flex flex-col text-left">
<span><span className="font-bold text-yellow-400">{dist.token}</span> sent to <span className="font-mono text-gray-300">{dist.address.substring(0, 12)}...</span></span>
<span className="font-mono text-xs text-gray-500">{dist.txHash}</span>
</div>
<span className="text-gray-400">{dist.time}</span>
</div>
))}
</div>
</div>
</div>
</div>
</Layout>
);
};
export default Faucet;
+68
View File
@@ -0,0 +1,68 @@
import React from 'react';
import Layout from '@/components/Layout';
import { Plus, MessageSquare, Eye, TrendingUp, Search } from 'lucide-react';
const Forum: React.FC = () => {
const topics = [
{ title: 'Proposal: New Treasury Spend for Marketing', user: 'Alice', avatar: '/avatars/avatar-1.png', category: 'Proposals', replies: 42, views: 1200, activity: '1h' },
{ title: 'Help with setting up a validator node', user: 'Bob', avatar: '/avatars/avatar-2.png', category: 'Technical Support', replies: 15, views: 850, activity: '3h' },
{ title: 'PezkuwiChain 2.0 Vision', user: 'Charlie', avatar: '/avatars/avatar-3.png', category: 'General Discussion', replies: 128, views: 5600, activity: '1d' },
{ title: 'Feature Request: Integrated NFT creator', user: 'David', avatar: '/avatars/avatar-4.png', category: 'Feature Requests', replies: 3, views: 250, activity: '2d' },
];
return (
<Layout>
<div className="container mx-auto px-4 py-8 text-white">
<div className="flex flex-col md:flex-row justify-between items-center mb-8">
<h1 className="text-4xl font-bold">Community Forum</h1>
<div className="flex items-center space-x-4 mt-4 md:mt-0">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" size={20} />
<input type="text" placeholder="Search forum..." className="w-full pl-10 pr-4 py-2 rounded-lg bg-gray-800 text-white focus:outline-none focus:ring-2 focus:ring-blue-500" />
</div>
<button className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-lg flex items-center transition-colors">
<Plus className="mr-2" size={20} /> New Topic
</button>
</div>
</div>
<div className="bg-gray-800 rounded-lg">
<div className="hidden md:grid grid-cols-12 gap-4 px-6 py-3 border-b border-gray-700 font-bold text-gray-400">
<div className="col-span-6">Topic</div>
<div className="col-span-2 text-center">Category</div>
<div className="col-span-1 text-center">Replies</div>
<div className="col-span-1 text-center">Views</div>
<div className="col-span-2 text-right">Activity</div>
</div>
{topics.map((topic, index) => (
<div key={index} className="grid grid-cols-12 gap-4 px-6 py-4 border-b border-gray-700 hover:bg-gray-700/50 transition-colors items-center">
<div className="col-span-12 md:col-span-6">
<a href="#" className="font-bold text-lg text-blue-400 hover:underline">{topic.title}</a>
<div className="flex items-center mt-1 text-sm text-gray-400">
<img src={topic.avatar} alt={topic.user} className="w-6 h-6 rounded-full mr-2" />
<span>{topic.user}</span>
</div>
</div>
<div className="col-span-6 md:col-span-2 text-left md:text-center">
<span className="font-semibold text-sm" style={{color: '#'+(Math.random()*0xFFFFFF<<0).toString(16)}}>{topic.category}</span>
</div>
<div className="col-span-2 md:col-span-1 text-left md:text-center flex items-center justify-start md:justify-center">
<MessageSquare size={16} className="mr-1 text-gray-500" /> {topic.replies}
</div>
<div className="col-span-2 md:col-span-1 text-left md:text-center flex items-center justify-start md:justify-center">
<Eye size={16} className="mr-1 text-gray-500" /> {topic.views}
</div>
<div className="col-span-4 md:col-span-2 text-left md:text-right text-gray-400">
{topic.activity}
</div>
</div>
))}
</div>
</div>
</Layout>
);
};
export default Forum;
+94
View File
@@ -0,0 +1,94 @@
import React from 'react';
import Layout from '@/components/Layout';
import { Award, Lightbulb, CheckCircle } from 'lucide-react';
const Grants: React.FC = () => {
const fundedProjects = [
{ name: 'Pezkuwi DEX', description: 'A fast and secure decentralized exchange built on PezkuwiChain.', logo: '/pezkuwimarket.png' },
{ name: 'NFT Marketplace', description: 'A platform for creating and trading NFTs with low transaction fees.', logo: '/PezkuwiExchange.png' },
{ name: 'DAO Governance Tool', description: 'A tool for decentralized autonomous organizations to manage their governance.', logo: '/governance.png' },
];
return (
<Layout>
<div className="container mx-auto px-4 py-8 text-white">
<div className="text-center mb-12">
<h1 className="text-5xl font-bold mb-2 text-purple-400">PezkuwiChain Grants Program</h1>
<p className="text-xl text-gray-400">Funding the future of the PezkuwiChain ecosystem.</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 text-center mb-12">
<div className="bg-gray-800 p-6 rounded-lg">
<Lightbulb className="mx-auto text-purple-400 mb-4" size={32} />
<h3 className="text-xl font-bold">Bring Your Idea</h3>
<p className="text-gray-400">We support innovative projects that bring value to the ecosystem.</p>
</div>
<div className="bg-gray-800 p-6 rounded-lg">
<Award className="mx-auto text-purple-400 mb-4" size={32} />
<h3 className="text-xl font-bold">Get Funded</h3>
<p className="text-gray-400">Receive funding and support to turn your idea into reality.</p>
</div>
<div className="bg-gray-800 p-6 rounded-lg">
<CheckCircle className="mx-auto text-purple-400 mb-4" size={32} />
<h3 className="text-xl font-bold">Grow the Ecosystem</h3>
<p className="text-gray-400">Contribute to the growth and decentralization of PezkuwiChain.</p>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<div className="bg-gray-800 p-8 rounded-lg">
<h2 className="text-3xl font-bold mb-6">Apply for a Grant</h2>
<form>
<div className="mb-4">
<label className="block mb-2 font-semibold">Project Name</label>
<input type="text" placeholder="Your awesome project" className="w-full p-3 rounded bg-gray-700 text-white focus:outline-none focus:ring-2 focus:ring-purple-500" />
</div>
<div className="mb-4">
<label className="block mb-2 font-semibold">Team Information</label>
<textarea placeholder="Tell us about your team" rows={3} className="w-full p-3 rounded bg-gray-700 text-white focus:outline-none focus:ring-2 focus:ring-purple-500"></textarea>
</div>
<div className="mb-6">
<label className="block mb-2 font-semibold">Requested Amount (USD)</label>
<input type="number" placeholder="10000" className="w-full p-3 rounded bg-gray-700 text-white focus:outline-none focus:ring-2 focus:ring-purple-500" />
</div>
<button className="w-full bg-purple-500 hover:bg-purple-600 text-white font-bold py-3 px-4 rounded-lg transition-colors">
Submit Application
</button>
</form>
</div>
<div>
<h2 className="text-3xl font-bold mb-6">Our Focus Areas</h2>
<ul className="list-disc list-inside space-y-3 text-lg">
<li>Decentralized Finance (DeFi)</li>
<li>NFTs and Gaming</li>
<li>Infrastructure and Tooling</li>
<li>Governance and DAOs</li>
<li>Privacy and Identity</li>
<li>Mobile and Web3 Applications</li>
</ul>
</div>
</div>
<div className="mt-16">
<h2 className="text-3xl font-bold text-center mb-8">Funded Projects</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{fundedProjects.map((project, index) => (
<div key={index} className="bg-gray-800 rounded-lg overflow-hidden">
<div className="p-6">
<div className="flex items-center mb-4">
<img src={project.logo} alt={`${project.name} logo`} className="w-12 h-12 mr-4"/>
<h3 className="text-xl font-bold">{project.name}</h3>
</div>
<p className="text-gray-400">{project.description}</p>
</div>
</div>
))}
</div>
</div>
</div>
</Layout>
);
};
export default Grants;
+160
View File
@@ -0,0 +1,160 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { ExternalLink, Globe, Book, Compass, FileCode, HandCoins, Users, Wrench, MessageCircle, GitFork, Server, Activity, Zap, Database, Radio, Layers, ArrowLeft } from 'lucide-react';
interface SubdomainItem {
name: string;
url: string;
description: string;
icon: React.ReactNode;
status: 'active' | 'coming-soon';
category: 'main' | 'rpc' | 'tools';
}
const subdomainList: SubdomainItem[] = [
// Main Services
{ name: 'Explorer', url: 'https://explorer.pezkuwichain.io', description: 'Block explorer and chain analytics', icon: <Compass className="w-6 h-6" />, status: 'active', category: 'main' },
{ name: 'Documentation', url: 'https://docs.pezkuwichain.io', description: 'SDK documentation and guides', icon: <Book className="w-6 h-6" />, status: 'active', category: 'main' },
{ name: 'Wiki', url: 'https://wiki.pezkuwichain.io', description: 'Community knowledge base', icon: <Globe className="w-6 h-6" />, status: 'active', category: 'main' },
{ name: 'Forum', url: 'https://forum.pezkuwichain.io', description: 'Community discussions and proposals', icon: <MessageCircle className="w-6 h-6" />, status: 'active', category: 'main' },
{ name: 'Faucet', url: 'https://faucet.pezkuwichain.io', description: 'Get testnet tokens for development', icon: <HandCoins className="w-6 h-6" />, status: 'active', category: 'main' },
{ name: 'Telemetry', url: 'https://telemetry.pezkuwichain.io', description: 'Network monitoring and node stats', icon: <Activity className="w-6 h-6" />, status: 'active', category: 'main' },
{ name: 'Developers', url: 'https://developers.pezkuwichain.io', description: 'Developer portal and resources', icon: <Users className="w-6 h-6" />, status: 'active', category: 'main' },
{ name: 'Grants', url: 'https://grants.pezkuwichain.io', description: 'Ecosystem grants program', icon: <Wrench className="w-6 h-6" />, status: 'active', category: 'main' },
{ name: 'API', url: 'https://api.pezkuwichain.io', description: 'REST API for chain data', icon: <FileCode className="w-6 h-6" />, status: 'active', category: 'main' },
// RPC Endpoints
{ name: 'Pezkuwi RPC', url: 'https://pezkuwichain-rpc.pezkuwichain.io', description: 'Primary mainnet RPC endpoint', icon: <Radio className="w-6 h-6" />, status: 'active', category: 'rpc' },
{ name: 'Beta RPC', url: 'https://beta-rpc.pezkuwichain.io', description: 'Beta testnet RPC endpoint', icon: <Zap className="w-6 h-6" />, status: 'active', category: 'rpc' },
{ name: 'Zagros Network', url: 'https://zagros.pezkuwichain.io', description: 'Zagros canary network', icon: <Globe className="w-6 h-6" />, status: 'active', category: 'rpc' },
{ name: 'Zagros RPC', url: 'https://zagros-rpc.pezkuwichain.io', description: 'Zagros relay chain RPC', icon: <Radio className="w-6 h-6" />, status: 'active', category: 'rpc' },
{ name: 'Asset Hub RPC', url: 'https://zagros-asset-hub-rpc.pezkuwichain.io', description: 'Zagros Asset Hub parachain RPC', icon: <Database className="w-6 h-6" />, status: 'active', category: 'rpc' },
{ name: 'Bridge Hub RPC', url: 'https://zagros-bridge-hub-rpc.pezkuwichain.io', description: 'Zagros Bridge Hub parachain RPC', icon: <Layers className="w-6 h-6" />, status: 'active', category: 'rpc' },
{ name: 'Collectives RPC', url: 'https://zagros-collectives-rpc.pezkuwichain.io', description: 'Zagros Collectives parachain RPC', icon: <Users className="w-6 h-6" />, status: 'active', category: 'rpc' },
{ name: 'Coretime RPC', url: 'https://zagros-coretime-rpc.pezkuwichain.io', description: 'Zagros Coretime parachain RPC', icon: <Server className="w-6 h-6" />, status: 'active', category: 'rpc' },
// Tools
{ name: 'Try Runtime', url: 'https://try-runtime.pezkuwichain.io', description: 'Test runtime upgrades safely', icon: <Zap className="w-6 h-6" />, status: 'active', category: 'tools' },
{ name: 'Try Runtime Zagros', url: 'https://try-runtime-zagros.pezkuwichain.io', description: 'Test runtime on Zagros canary', icon: <Zap className="w-6 h-6" />, status: 'active', category: 'tools' },
{ name: 'Network Wiki', url: 'https://wiki.network.pezkuwichain.io', description: 'Technical network documentation', icon: <Book className="w-6 h-6" />, status: 'active', category: 'tools' },
];
const Subdomains: React.FC = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const mainServices = subdomainList.filter(s => s.category === 'main');
const rpcEndpoints = subdomainList.filter(s => s.category === 'rpc');
const tools = subdomainList.filter(s => s.category === 'tools');
const renderCard = (item: SubdomainItem) => (
<a
key={item.name}
href={item.url}
target="_blank"
rel="noopener noreferrer"
className="group flex items-start p-4 bg-gray-900/50 border border-gray-800 rounded-xl hover:border-green-500/50 hover:bg-gray-900 transition-all duration-300"
>
<div className="p-3 rounded-lg bg-gradient-to-br from-green-500/20 to-cyan-500/20 text-green-400 group-hover:from-green-500/30 group-hover:to-cyan-500/30 transition-all">
{item.icon}
</div>
<div className="ml-4 flex-1">
<div className="flex items-center justify-between">
<h3 className="text-white font-semibold group-hover:text-green-400 transition-colors">
{item.name}
</h3>
<ExternalLink className="w-4 h-4 text-gray-500 group-hover:text-green-400 transition-colors" />
</div>
<p className="text-gray-400 text-sm mt-1">{item.description}</p>
<code className="text-xs text-cyan-400/70 mt-2 block truncate">{item.url}</code>
</div>
</a>
);
return (
<div className="min-h-screen bg-gray-950 py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Back Button */}
<button
onClick={() => navigate('/')}
className="flex items-center text-gray-400 hover:text-white mb-8 transition-colors"
>
<ArrowLeft className="w-5 h-5 mr-2" />
Back to Home
</button>
{/* Header */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold mb-4 bg-gradient-to-r from-green-400 via-cyan-400 to-purple-400 bg-clip-text text-transparent">
PezkuwiChain Subdomains
</h1>
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
Access all PezkuwiChain services, RPC endpoints, and developer tools
</p>
</div>
{/* Main Services */}
<section className="mb-12">
<h2 className="text-2xl font-bold text-white mb-6 flex items-center">
<Globe className="w-6 h-6 mr-3 text-green-400" />
Main Services
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{mainServices.map(renderCard)}
</div>
</section>
{/* RPC Endpoints */}
<section className="mb-12">
<h2 className="text-2xl font-bold text-white mb-6 flex items-center">
<Radio className="w-6 h-6 mr-3 text-cyan-400" />
RPC Endpoints
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{rpcEndpoints.map(renderCard)}
</div>
</section>
{/* Developer Tools */}
<section className="mb-12">
<h2 className="text-2xl font-bold text-white mb-6 flex items-center">
<Wrench className="w-6 h-6 mr-3 text-purple-400" />
Developer Tools
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{tools.map(renderCard)}
</div>
</section>
{/* Info Box */}
<div className="bg-gradient-to-r from-green-500/10 to-cyan-500/10 border border-green-500/20 rounded-xl p-6 text-center">
<h3 className="text-xl font-semibold text-white mb-2">Need Help?</h3>
<p className="text-gray-400 mb-4">
Check our documentation or join the community forum for support
</p>
<div className="flex justify-center gap-4">
<a
href="https://docs.pezkuwichain.io"
target="_blank"
rel="noopener noreferrer"
className="px-6 py-2 bg-green-500 hover:bg-green-600 text-white font-semibold rounded-lg transition-colors"
>
Read Docs
</a>
<a
href="https://forum.pezkuwichain.io"
target="_blank"
rel="noopener noreferrer"
className="px-6 py-2 bg-gray-800 hover:bg-gray-700 text-white font-semibold rounded-lg border border-gray-700 transition-colors"
>
Join Forum
</a>
</div>
</div>
</div>
</div>
);
};
export default Subdomains;
+89
View File
@@ -0,0 +1,89 @@
import React from 'react';
import Layout from '@/components/Layout';
import { CheckCircle, Zap, Server, Globe } from 'lucide-react';
const Telemetry: React.FC = () => {
const validators = [
{ name: 'Validator A', stake: '1.2M HEZ', uptime: 99.98, status: 'Active' },
{ name: 'Validator B', stake: '1.1M HEZ', uptime: 99.95, status: 'Active' },
{ name: 'Validator C', stake: '1.0M HEZ', uptime: 99.92, status: 'Active' },
{ name: 'Validator D', stake: '0.9M HEZ', uptime: 99.80, status: 'Active' },
{ name: 'Validator E', stake: '0.8M HEZ', uptime: 100, status: 'Waiting' },
];
return (
<Layout>
<div className="container mx-auto px-4 py-8 text-white">
<h1 className="text-4xl font-bold mb-8">Network Telemetry</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div className="bg-gray-800 p-6 rounded-lg">
<h3 className="text-lg font-semibold text-gray-400">Best Finalized Block</h3>
<p className="text-2xl font-bold">1,234,567</p>
</div>
<div className="bg-gray-800 p-6 rounded-lg">
<h3 className="text-lg font-semibold text-gray-400">Average Block Time</h3>
<p className="text-2xl font-bold">6.0s</p>
</div>
<div className="bg-gray-800 p-6 rounded-lg">
<h3 className="text-lg font-semibold text-gray-400">Active Nodes</h3>
<p className="text-2xl font-bold">1,234</p>
</div>
<div className="bg-gray-800 p-6 rounded-lg">
<h3 className="text-lg font-semibold text-gray-400">Validators</h3>
<p className="text-2xl font-bold">42</p>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2 bg-gray-800 p-6 rounded-lg">
<h2 className="text-2xl font-bold mb-4 flex items-center">
<Server className="mr-3 text-blue-400" />
Validators
</h2>
<table className="w-full text-left">
<thead>
<tr className="border-b border-gray-700">
<th className="py-2">Name</th>
<th className="py-2 text-right">Total Stake</th>
<th className="py-2 text-right">Uptime</th>
<th className="py-2 text-right">Status</th>
</tr>
</thead>
<tbody>
{validators.map((validator, index) => (
<tr key={index} className="border-b border-gray-700">
<td className="py-3 font-semibold">{validator.name}</td>
<td className="py-3 text-right">{validator.stake}</td>
<td className="py-3 text-right">{validator.uptime}%</td>
<td className="py-3 text-right">
<span className={`px-2 py-1 text-xs font-bold rounded-full ${validator.status === 'Active' ? 'bg-green-500/20 text-green-400' : 'bg-yellow-500/20 text-yellow-400'}`}>
{validator.status}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="bg-gray-800 p-6 rounded-lg">
<h2 className="text-2xl font-bold mb-4 flex items-center">
<Globe className="mr-3 text-green-400" />
Node Map
</h2>
<div className="h-64 bg-gray-700 rounded-lg flex items-center justify-center">
<p className="text-gray-500">Node map visualization placeholder</p>
</div>
<p className="text-sm text-gray-400 mt-4">
Note: A fully functional telemetry site already exists at <a href="https://telemetry.pezkuwichain.io/" target="_blank" rel="noopener noreferrer" className="text-blue-400 underline">telemetry.pezkuwichain.io</a>.
This page is a placeholder for a new, integrated design.
</p>
</div>
</div>
</div>
</Layout>
);
};
export default Telemetry;
+110
View File
@@ -0,0 +1,110 @@
import React from 'react';
import Layout from '@/components/Layout';
import { ArrowUpRight, ArrowDownLeft, Send, QrCode } from 'lucide-react';
const Wallet: React.FC = () => {
const isConnected = false; // Mock connection status
return (
<Layout>
<div className="container mx-auto px-4 py-8 text-white">
<div className="flex justify-between items-center mb-8">
<h1 className="text-4xl font-bold text-green-400">Web Wallet</h1>
{!isConnected && (
<button className="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg transition-colors">
Connect Wallet
</button>
)}
</div>
<div className="bg-gray-800 p-6 rounded-lg mb-8">
<h2 className="text-2xl font-bold mb-4">My Balance</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="flex items-center space-x-3">
<img src="/hez_logo_kurdistangunesi.png" alt="HEZ" className="w-10 h-10" />
<div>
<p className="text-gray-400">HEZ</p>
<p className="text-xl font-bold">1,234.56</p>
</div>
</div>
<div className="flex items-center space-x-3">
<img src="/pez_logo.jpg" alt="PEZ" className="w-10 h-10" />
<div>
<p className="text-gray-400">PEZ</p>
<p className="text-xl font-bold">5,000.00</p>
</div>
</div>
<div className="flex items-center space-x-3">
<img src="/usdt(hez)logo.png" alt="wHEZ" className="w-10 h-10" />
<div>
<p className="text-gray-400">wHEZ</p>
<p className="text-xl font-bold">100.00</p>
</div>
</div>
</div>
<div className="flex space-x-4 mt-6">
<button className="flex items-center justify-center w-full bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-4 rounded-lg transition-colors">
<Send className="mr-2" size={20} /> Send
</button>
<button className="flex items-center justify-center w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-4 rounded-lg transition-colors">
<QrCode className="mr-2" size={20} /> Receive
</button>
</div>
</div>
<div>
<h2 className="text-2xl font-bold mb-4">Transaction History</h2>
<div className="space-y-4">
<div className="bg-gray-800 p-4 rounded-lg flex items-center justify-between">
<div className="flex items-center">
<div className="bg-red-500/20 p-2 rounded-full mr-4">
<ArrowUpRight className="text-red-400" size={20} />
</div>
<div>
<p className="font-semibold">Sent HEZ</p>
<p className="text-sm text-gray-400">To: 5FHne...w1s</p>
</div>
</div>
<div className="text-right">
<p className="font-bold">-10.00 HEZ</p>
<p className="text-sm text-gray-400">3 hours ago</p>
</div>
</div>
<div className="bg-gray-800 p-4 rounded-lg flex items-center justify-between">
<div className="flex items-center">
<div className="bg-green-500/20 p-2 rounded-full mr-4">
<ArrowDownLeft className="text-green-400" size={20} />
</div>
<div>
<p className="font-semibold">Received PEZ</p>
<p className="text-sm text-gray-400">From: 5Grwva...c1s</p>
</div>
</div>
<div className="text-right">
<p className="font-bold">+50.00 PEZ</p>
<p className="text-sm text-gray-400">1 day ago</p>
</div>
</div>
<div className="bg-gray-800 p-4 rounded-lg flex items-center justify-between">
<div className="flex items-center">
<div className="bg-red-500/20 p-2 rounded-full mr-4">
<ArrowUpRight className="text-red-400" size={20} />
</div>
<div>
<p className="font-semibold">Sent wHEZ</p>
<p className="text-sm text-gray-400">To: 5E5s3...e3s</p>
</div>
</div>
<div className="text-right">
<p className="font-bold">-5.00 wHEZ</p>
<p className="text-sm text-gray-400">2 days ago</p>
</div>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default Wallet;
+78
View File
@@ -0,0 +1,78 @@
import React from 'react';
import Layout from '@/components/Layout';
import { Search, BookOpen, Star } from 'lucide-react';
const Wiki: React.FC = () => {
const categories = [
{ name: 'General', description: 'Learn the basics of PezkuwiChain.' },
{ name: 'Validators', description: 'How to run a validator node and secure the network.' },
{ name: 'Developers', description: 'Guides and resources for building on PezkuwiChain.' },
{ name: 'Tokenomics', description: 'Understand the economics of the HEZ and PEZ tokens.' },
{ name: 'Governance', description: 'Participate in the decentralized governance of the network.' },
{ name: 'Wallets', description: 'Learn how to use the official and third-party wallets.' },
];
const popularArticles = [
'How to become a validator',
'PezkuwiChain tokenomics explained',
'Connecting to the network with Polkadot.js',
'Understanding the governance process',
];
return (
<Layout>
<div className="container mx-auto px-4 py-8 text-white">
<div className="text-center mb-12">
<h1 className="text-5xl font-bold mb-2">Community Wiki</h1>
<p className="text-xl text-gray-400">Your community-driven knowledge base for all things PezkuwiChain.</p>
<div className="mt-6 max-w-2xl mx-auto">
<div className="relative">
<Search className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400" size={24} />
<input
type="text"
placeholder="Search the wiki..."
className="w-full pl-14 pr-4 py-3 rounded-lg bg-gray-800 text-white text-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2">
<h2 className="text-3xl font-bold mb-6 flex items-center">
<BookOpen className="mr-3 text-blue-400" />
Categories
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{categories.map((category, index) => (
<div key={index} className="bg-gray-800 p-6 rounded-lg hover:bg-gray-700 transition-colors cursor-pointer">
<h3 className="text-xl font-bold mb-2 text-blue-400">{category.name}</h3>
<p className="text-gray-400">{category.description}</p>
</div>
))}
</div>
</div>
<div>
<h2 className="text-3xl font-bold mb-6 flex items-center">
<Star className="mr-3 text-yellow-400" />
Popular Articles
</h2>
<div className="bg-gray-800 p-6 rounded-lg">
<ul className="space-y-4">
{popularArticles.map((article, index) => (
<li key={index}>
<a href="#" className="hover:text-blue-400 transition-colors">{article}</a>
</li>
))}
</ul>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default Wiki;
+1 -1
View File
@@ -19,7 +19,7 @@ export default function CreatePresale() {
// Form state
const [formData, setFormData] = useState({
paymentAsset: '2', // wUSDT
paymentAsset: '1000', // wUSDT
rewardAsset: '1', // PEZ (or custom)
tokensForSale: '10000000', // 10M tokens for sale (with 6 decimals = 10M)
durationDays: '45',
+1 -1
View File
@@ -227,7 +227,7 @@ export default function PresaleDetail() {
return currentBlock <= graceEnd;
};
const wusdtBalance = balances.find((b) => b.assetId === 2)?.balance || '0';
const wusdtBalance = balances.find((b) => b.assetId === 1000)?.balance || '0';
return (
<div className="container mx-auto px-4 py-8 max-w-6xl">
+31
View File
@@ -0,0 +1,31 @@
import React from 'react';
import NetworkPage from './NetworkPage';
const Alfa: React.FC = () => {
return (
<NetworkPage
id="alfa"
name="Alfa Testnet"
type="Development"
description="Early-stage testing network for experimental features and internal testing"
endpoint="wss://alfa.pezkuwichain.io"
chainId="pezkuwichain-alfa"
validators={5}
features={[
'Experimental Features',
'Early Access',
'Internal Testing',
'Rapid Changes',
'Developer Preview',
'Unstable'
]}
color="from-red-600 to-red-800"
status="coming-soon"
blockTime="~6s"
finality="~12s"
consensus="TNPoS"
/>
);
};
export default Alfa;
+31
View File
@@ -0,0 +1,31 @@
import React from 'react';
import NetworkPage from './NetworkPage';
const Beta: React.FC = () => {
return (
<NetworkPage
id="beta"
name="Beta Testnet"
type="Development"
description="Active development testnet with the latest features and improvements"
endpoint="wss://rpc.pezkuwichain.io:9944"
chainId="pezkuwichain-beta"
validators={7}
features={[
'Latest Features',
'Active Development',
'Fast Iterations',
'Community Access',
'Bug Hunting',
'Feature Feedback'
]}
color="from-orange-600 to-orange-800"
status="active"
blockTime="~6s"
finality="~12s"
consensus="TNPoS"
/>
);
};
export default Beta;
+31
View File
@@ -0,0 +1,31 @@
import React from 'react';
import NetworkPage from './NetworkPage';
const Development: React.FC = () => {
return (
<NetworkPage
id="development"
name="Development Environment"
type="Development"
description="Internal development network for core team feature development"
endpoint="wss://dev.pezkuwichain.io"
chainId="pezkuwichain-dev"
validators={3}
features={[
'Core Development',
'Feature Prototyping',
'Internal Only',
'Frequent Resets',
'Debug Mode',
'Hot Reloading'
]}
color="from-yellow-600 to-yellow-800"
status="coming-soon"
blockTime="~6s"
finality="~12s"
consensus="TNPoS"
/>
);
};
export default Development;
+31
View File
@@ -0,0 +1,31 @@
import React from 'react';
import NetworkPage from './NetworkPage';
const Local: React.FC = () => {
return (
<NetworkPage
id="local"
name="Local Testnet"
type="Local"
description="Run your own local PezkuwiChain node for development and testing"
endpoint="ws://127.0.0.1:9944"
chainId="pezkuwichain-local"
validators={1}
features={[
'Local Development',
'Instant Blocks',
'Full Control',
'No Network Latency',
'Custom Genesis',
'Unlimited Funds'
]}
color="from-blue-600 to-blue-800"
status="active"
blockTime="Instant"
finality="Instant"
consensus="Dev"
/>
);
};
export default Local;
+33
View File
@@ -0,0 +1,33 @@
import React from 'react';
import NetworkPage from './NetworkPage';
const Mainnet: React.FC = () => {
return (
<NetworkPage
id="mainnet"
name="PezkuwiChain Mainnet"
type="Live"
description="The production PezkuwiChain network with real value transactions"
endpoint="wss://mainnet.pezkuwichain.io"
chainId="pezkuwichain-mainnet"
validators={21}
features={[
'HEZ Native Token',
'Production Ready',
'Full Security',
'Governance',
'Staking',
'TNPoS Consensus',
'Identity & KYC',
'Asset Hub'
]}
color="from-green-600 to-green-800"
status="coming-soon"
blockTime="~6s"
finality="~12s"
consensus="TNPoS"
/>
);
};
export default Mainnet;
+236
View File
@@ -0,0 +1,236 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { ArrowLeft, Copy, Check, Server, Globe, Zap, Clock, Shield, ExternalLink, Activity } from 'lucide-react';
interface NetworkPageProps {
id: string;
name: string;
type: 'Live' | 'Development' | 'Local';
description: string;
endpoint: string;
chainId: string;
validators: number;
features: string[];
color: string;
status: 'active' | 'coming-soon' | 'maintenance';
blockTime?: string;
finality?: string;
consensus?: string;
}
const NetworkPage: React.FC<NetworkPageProps> = ({
id,
name,
type,
description,
endpoint,
chainId,
validators,
features,
color,
status,
blockTime = '~6s',
finality = '~12s',
consensus = 'TNPoS'
}) => {
const navigate = useNavigate();
const [copiedField, setCopiedField] = useState<string | null>(null);
const copyToClipboard = (text: string, field: string) => {
navigator.clipboard.writeText(text);
setCopiedField(field);
setTimeout(() => setCopiedField(null), 2000);
};
const statusColors = {
'active': 'bg-green-500/20 text-green-400 border-green-500/30',
'coming-soon': 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30',
'maintenance': 'bg-red-500/20 text-red-400 border-red-500/30'
};
const statusLabels = {
'active': 'Active',
'coming-soon': 'Coming Soon',
'maintenance': 'Maintenance'
};
const typeColors = {
'Live': 'bg-green-900/30 text-green-400',
'Development': 'bg-yellow-900/30 text-yellow-400',
'Local': 'bg-blue-900/30 text-blue-400'
};
return (
<div className="min-h-screen bg-gray-950 py-12">
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Back Button */}
<button
onClick={() => navigate('/')}
className="flex items-center text-gray-400 hover:text-white mb-8 transition-colors"
>
<ArrowLeft className="w-5 h-5 mr-2" />
Back to Home
</button>
{/* Header */}
<div className="mb-10">
<div className="flex items-center gap-4 mb-4">
<div className={`p-4 rounded-xl bg-gradient-to-br ${color}`}>
<Globe className="w-8 h-8 text-white" />
</div>
<div>
<div className="flex items-center gap-3">
<h1 className="text-3xl md:text-4xl font-bold text-white">{name}</h1>
<span className={`px-3 py-1 text-sm rounded-full ${typeColors[type]}`}>
{type}
</span>
</div>
<p className="text-gray-400 mt-1">{description}</p>
</div>
</div>
<div className={`inline-flex items-center px-4 py-2 rounded-full border ${statusColors[status]}`}>
<span className="w-2 h-2 rounded-full bg-current mr-2 animate-pulse"></span>
{statusLabels[status]}
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-10">
<div className="bg-gray-900/50 border border-gray-800 rounded-xl p-5 text-center">
<Server className="w-6 h-6 text-cyan-400 mx-auto mb-2" />
<div className="text-2xl font-bold text-white">{validators}</div>
<div className="text-gray-400 text-sm">Validators</div>
</div>
<div className="bg-gray-900/50 border border-gray-800 rounded-xl p-5 text-center">
<Clock className="w-6 h-6 text-green-400 mx-auto mb-2" />
<div className="text-2xl font-bold text-white">{blockTime}</div>
<div className="text-gray-400 text-sm">Block Time</div>
</div>
<div className="bg-gray-900/50 border border-gray-800 rounded-xl p-5 text-center">
<Zap className="w-6 h-6 text-yellow-400 mx-auto mb-2" />
<div className="text-2xl font-bold text-white">{finality}</div>
<div className="text-gray-400 text-sm">Finality</div>
</div>
<div className="bg-gray-900/50 border border-gray-800 rounded-xl p-5 text-center">
<Shield className="w-6 h-6 text-purple-400 mx-auto mb-2" />
<div className="text-2xl font-bold text-white">{consensus}</div>
<div className="text-gray-400 text-sm">Consensus</div>
</div>
</div>
{/* Connection Details */}
<div className="bg-gray-900/50 border border-gray-800 rounded-xl p-6 mb-8">
<h2 className="text-xl font-semibold text-white mb-6 flex items-center">
<Activity className="w-5 h-5 mr-2 text-cyan-400" />
Connection Details
</h2>
<div className="space-y-4">
<div>
<label className="text-gray-400 text-sm block mb-2">WebSocket Endpoint</label>
<div className="flex items-center">
<code className="flex-1 p-4 bg-gray-950 rounded-lg text-cyan-400 font-mono text-sm border border-gray-800">
{endpoint}
</code>
<button
onClick={() => copyToClipboard(endpoint, 'endpoint')}
className="ml-3 p-3 text-gray-400 hover:text-white bg-gray-800 rounded-lg transition-colors"
>
{copiedField === 'endpoint' ?
<Check className="w-5 h-5 text-green-400" /> :
<Copy className="w-5 h-5" />
}
</button>
</div>
</div>
<div>
<label className="text-gray-400 text-sm block mb-2">Chain ID</label>
<div className="flex items-center">
<code className="flex-1 p-4 bg-gray-950 rounded-lg text-purple-400 font-mono text-sm border border-gray-800">
{chainId}
</code>
<button
onClick={() => copyToClipboard(chainId, 'chainId')}
className="ml-3 p-3 text-gray-400 hover:text-white bg-gray-800 rounded-lg transition-colors"
>
{copiedField === 'chainId' ?
<Check className="w-5 h-5 text-green-400" /> :
<Copy className="w-5 h-5" />
}
</button>
</div>
</div>
</div>
</div>
{/* Features */}
<div className="bg-gray-900/50 border border-gray-800 rounded-xl p-6 mb-8">
<h2 className="text-xl font-semibold text-white mb-4">Features</h2>
<div className="flex flex-wrap gap-3">
{features.map((feature, index) => (
<span
key={index}
className={`px-4 py-2 rounded-full bg-gradient-to-r ${color} text-white text-sm font-medium`}
>
{feature}
</span>
))}
</div>
</div>
{/* Code Example */}
<div className="bg-gray-900/50 border border-gray-800 rounded-xl p-6 mb-8">
<h2 className="text-xl font-semibold text-white mb-4">Quick Start</h2>
<div className="bg-gray-950 rounded-lg p-4 border border-gray-800">
<pre className="text-sm overflow-x-auto">
<code className="text-gray-300">
{`import { ApiPromise, WsProvider } from '@polkadot/api';
const provider = new WsProvider('${endpoint}');
const api = await ApiPromise.create({ provider });
// Get chain info
const chain = await api.rpc.system.chain();
console.log('Connected to:', chain.toString());`}
</code>
</pre>
</div>
</div>
{/* Action Buttons */}
<div className="flex flex-wrap gap-4">
<a
href="https://explorer.pezkuwichain.io"
target="_blank"
rel="noopener noreferrer"
className={`flex items-center px-6 py-3 rounded-lg bg-gradient-to-r ${color} text-white font-semibold hover:opacity-90 transition-opacity`}
>
<ExternalLink className="w-5 h-5 mr-2" />
Open Explorer
</a>
<a
href="https://docs.pezkuwichain.io"
target="_blank"
rel="noopener noreferrer"
className="flex items-center px-6 py-3 rounded-lg bg-gray-800 text-white font-semibold hover:bg-gray-700 transition-colors border border-gray-700"
>
Documentation
</a>
{type !== 'Live' && (
<a
href="https://faucet.pezkuwichain.io"
target="_blank"
rel="noopener noreferrer"
className="flex items-center px-6 py-3 rounded-lg bg-cyan-600 text-white font-semibold hover:bg-cyan-700 transition-colors"
>
Get Test Tokens
</a>
)}
</div>
</div>
</div>
);
};
export default NetworkPage;
+31
View File
@@ -0,0 +1,31 @@
import React from 'react';
import NetworkPage from './NetworkPage';
const Staging: React.FC = () => {
return (
<NetworkPage
id="staging"
name="Staging Network"
type="Development"
description="Pre-production environment for final testing before mainnet deployment"
endpoint="wss://staging.pezkuwichain.io"
chainId="pezkuwichain-staging"
validators={14}
features={[
'Pre-Production Testing',
'Runtime Upgrades',
'Feature Validation',
'Security Audits',
'Stress Testing',
'Migration Testing'
]}
color="from-purple-600 to-purple-800"
status="coming-soon"
blockTime="~6s"
finality="~12s"
consensus="TNPoS"
/>
);
};
export default Staging;
+31
View File
@@ -0,0 +1,31 @@
import React from 'react';
import NetworkPage from './NetworkPage';
const Testnet: React.FC = () => {
return (
<NetworkPage
id="testnet"
name="Public Testnet"
type="Development"
description="Public testing network for community and developers to test applications"
endpoint="wss://testnet.pezkuwichain.io"
chainId="pezkuwichain-testnet"
validators={10}
features={[
'Free Test Tokens',
'Public Access',
'Full Feature Set',
'Community Testing',
'dApp Development',
'No Real Value'
]}
color="from-cyan-600 to-cyan-800"
status="coming-soon"
blockTime="~6s"
finality="~12s"
consensus="TNPoS"
/>
);
};
export default Testnet;