diff --git a/web/src/components/AccountBalance.tsx b/web/src/components/AccountBalance.tsx index 5db770c6..680636c1 100644 --- a/web/src/components/AccountBalance.tsx +++ b/web/src/components/AccountBalance.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Wallet, TrendingUp, RefreshCw, Award, Plus, Coins, Send, Shield, Users, Fuel, Lock } from 'lucide-react'; @@ -21,6 +22,7 @@ interface TokenBalance { } export const AccountBalance: React.FC = () => { + const { t } = useTranslation(); const { api, assetHubApi, peopleApi, isApiReady, isAssetHubReady, isPeopleReady, selectedAccount } = usePezkuwi(); const [balance, setBalance] = useState<{ free: string; @@ -526,7 +528,7 @@ export const AccountBalance: React.FC = () => { // Add custom token handler const handleAddToken = async (assetId: number) => { if (customTokenIds.includes(assetId)) { - alert('Token already added!'); + alert(t('balance.tokenAlreadyAdded')); return; } @@ -683,7 +685,7 @@ export const AccountBalance: React.FC = () => {
-

Connect your wallet to view balance

+

{t('balance.connectWallet')}

@@ -700,9 +702,9 @@ export const AccountBalance: React.FC = () => { HEZ
- HEZ Balance + {t('balance.hezBalance')} -
Multi-Chain Overview
+
{t('balance.multiChain')}
@@ -714,9 +716,9 @@ export const AccountBalance: React.FC = () => { title="Send HEZ to teyrcahins for transaction fees" > - Add Fee + {t('balance.addFee')} - Send HEZ to Asset Hub / People Chain + {t('balance.sendHezToTeyrcahins')}
{hezUsdPrice > 0 - ? `≈ $${((parseFloat(balance.total) + parseFloat(assetHubHezBalance) + parseFloat(peopleHezBalance)) * hezUsdPrice).toFixed(2)} USD (Toplam)` - : 'Price loading...'} + ? t('balance.usdTotal', { amount: ((parseFloat(balance.total) + parseFloat(assetHubHezBalance) + parseFloat(peopleHezBalance)) * hezUsdPrice).toFixed(2) }) + : t('balance.priceLoading')}
@@ -753,11 +755,11 @@ export const AccountBalance: React.FC = () => {
- Pezkuwi (Relay Chain) + {t('balance.relayChain')}
{balance.free} HEZ
-
Reserved: {balance.reserved}
+
{t('balance.reserved', { amount: balance.reserved })}
@@ -767,13 +769,13 @@ export const AccountBalance: React.FC = () => {
- Pezkuwi Asset Hub - (PEZ fees) + {t('balance.assetHub')} + {t('balance.pezFees')}
{assetHubHezBalance} HEZ
{parseFloat(assetHubHezBalance) < 0.1 && ( -
⚠️ Low for fees
+
{`⚠️ ${t('balance.lowForFees')}`}
)}
@@ -784,13 +786,13 @@ export const AccountBalance: React.FC = () => {
- Pezkuwi People - (Identity fees) + {t('balance.peopleChain')} + {t('balance.identityFees')}
{peopleHezBalance} HEZ
{parseFloat(peopleHezBalance) < 0.1 && ( -
⚠️ Low for fees
+
{`⚠️ ${t('balance.lowForFees')}`}
)}
@@ -807,7 +809,7 @@ export const AccountBalance: React.FC = () => {
PEZ - PEZ Balance + {t('balance.pezBalance')}
@@ -834,10 +836,10 @@ export const AccountBalance: React.FC = () => {
{pezUsdPrice > 0 ? `≈ $${(parseFloat(pezBalance) * pezUsdPrice).toFixed(2)} USD` - : 'Price loading...'} + : t('balance.priceLoading')}
- Governance & Rewards Token (on Asset Hub) + {t('balance.govRewardsToken')}
@@ -849,7 +851,7 @@ export const AccountBalance: React.FC = () => {
USDT - USDT Balance + {t('balance.usdtBalance')}
@@ -860,7 +862,7 @@ export const AccountBalance: React.FC = () => { USDT
- ≈ ${usdtBalance} USD • Stablecoin (on Asset Hub) + {t('balance.stablecoinOnAssetHub', { amount: usdtBalance })}
@@ -873,7 +875,7 @@ export const AccountBalance: React.FC = () => {
wHEZ - wHEZ Balance + {t('balance.whezBalance')}
@@ -886,7 +888,7 @@ export const AccountBalance: React.FC = () => {
{hezUsdPrice > 0 ? `≈ $${(parseFloat(whezBalance) * hezUsdPrice).toFixed(2)} USD` - : 'Price loading...'} • Wrapped HEZ (on Asset Hub) + : t('balance.priceLoading')} • {t('balance.wrappedHezOnAssetHub')}
@@ -900,7 +902,7 @@ export const AccountBalance: React.FC = () => {
LP - LP Token Positions + {t('balance.lpTokenPositions')}
@@ -918,7 +920,7 @@ export const AccountBalance: React.FC = () => {
{lp.balance}
-
Pool Share
+
{t('balance.poolShare')}
@@ -943,7 +945,7 @@ export const AccountBalance: React.FC = () => { - Account Information + {t('balance.accountInfo')} @@ -951,13 +953,13 @@ export const AccountBalance: React.FC = () => { {/* Account Details */}
- Account + {t('balance.account')} {selectedAccount.meta.name || 'Unnamed'}
- Address + {t('balance.address')} {selectedAccount.address.slice(0, 8)}...{selectedAccount.address.slice(-8)} @@ -966,9 +968,9 @@ export const AccountBalance: React.FC = () => { {/* Scores from Blockchain */}
-
Scores from Blockchain
+
{t('balance.scoresFromBlockchain')}
{loadingScores ? ( -
Loading scores...
+
{t('balance.loadingScores')}
) : (
{/* Score Grid */} @@ -976,28 +978,28 @@ export const AccountBalance: React.FC = () => {
- Trust + {t('balance.trust')}
{scores.trustScore}
- Referral + {t('balance.referral')}
{scores.referralScore}
- Staking + {t('balance.staking')}
{scores.stakingScore}
- Tiki + {t('balance.tiki')}
{scores.tikiScore}
@@ -1006,7 +1008,7 @@ export const AccountBalance: React.FC = () => { {/* Total Score */}
- Total Score + {t('balance.totalScore')} {scores.totalScore} @@ -1026,7 +1028,7 @@ export const AccountBalance: React.FC = () => {
- Other Assets + {t('balance.otherAssets')}
@@ -1044,9 +1046,9 @@ export const AccountBalance: React.FC = () => { {otherTokens.length === 0 ? (
-

No custom tokens yet

+

{t('balance.noCustomTokens')}

- Add custom tokens to track additional assets + {t('balance.addCustomTokensDesc')}

) : ( diff --git a/web/src/components/AddTokenModal.tsx b/web/src/components/AddTokenModal.tsx index 285e22bf..717db3cc 100644 --- a/web/src/components/AddTokenModal.tsx +++ b/web/src/components/AddTokenModal.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { Dialog, DialogContent, @@ -31,6 +32,7 @@ export const AddTokenModal: React.FC = ({ onClose, onAddToken, }) => { + const { t } = useTranslation(); const { assetHubApi, isAssetHubReady } = usePezkuwi(); const [assetId, setAssetId] = useState(''); const [error, setError] = useState(''); @@ -56,12 +58,12 @@ export const AddTokenModal: React.FC = ({ const id = parseInt(assetId); if (isNaN(id) || id < 0) { - setError('Please enter a valid asset ID (positive number)'); + setError(t('addToken.invalidId')); return; } if (!assetHubApi || !isAssetHubReady) { - setError('Asset Hub connection not ready. Please wait...'); + setError(t('addToken.notReady')); return; } @@ -71,7 +73,7 @@ export const AddTokenModal: React.FC = ({ const assetInfo = await assetHubApi.query.assets.asset(id); if (!assetInfo || assetInfo.isNone) { - setError(`Asset #${id} not found on blockchain`); + setError(t('addToken.notFound', { id })); setIsSearching(false); return; } @@ -104,7 +106,7 @@ export const AddTokenModal: React.FC = ({ }); } catch (err) { console.error('Failed to fetch asset:', err); - setError('Failed to fetch asset from blockchain'); + setError(t('addToken.fetchFailed')); } finally { setIsSearching(false); } @@ -118,7 +120,7 @@ export const AddTokenModal: React.FC = ({ await onAddToken(tokenInfo.assetId); handleClose(); } catch { - setError('Failed to add token'); + setError(t('addToken.addFailed')); } finally { setIsAdding(false); } @@ -142,9 +144,9 @@ export const AddTokenModal: React.FC = ({ - Add Custom Token + {t('addToken.title')} - Enter the asset ID to fetch token details from blockchain. + {t('addToken.description')} @@ -152,7 +154,7 @@ export const AddTokenModal: React.FC = ({ {/* Search Input */}
= ({ setError(''); }} onKeyPress={handleKeyPress} - placeholder="e.g., 1001" + placeholder={t('addToken.assetIdPlaceholder')} className="bg-gray-800 border-gray-700 text-white placeholder:text-gray-500 flex-1" min="0" /> @@ -183,7 +185,7 @@ export const AddTokenModal: React.FC = ({

- Known assets: 1001 (DOT), 1002 (ETH), 1003 (BTC) + {t('addToken.assetIdHelp')}

@@ -192,23 +194,23 @@ export const AddTokenModal: React.FC = ({
- Token Found! + {t('addToken.found')}
- Symbol: + {t('addToken.symbol')}: {tokenInfo.symbol}
- Name: + {t('addToken.name')}: {tokenInfo.name}
- Decimals: + {t('addToken.decimals')}: {tokenInfo.decimals}
- Asset ID: + {t('addToken.assetId')}: #{tokenInfo.assetId}
@@ -232,7 +234,7 @@ export const AddTokenModal: React.FC = ({ disabled={isAdding} className="border border-gray-700 hover:bg-gray-800" > - Cancel + {t('addToken.cancel')}
diff --git a/web/src/components/AppLayout.tsx b/web/src/components/AppLayout.tsx index 08b4a98c..0bc29d6c 100644 --- a/web/src/components/AppLayout.tsx +++ b/web/src/components/AppLayout.tsx @@ -110,14 +110,14 @@ const AppLayout: React.FC = () => { className="text-cyan-300 hover:text-cyan-100 transition-colors flex items-center gap-1 text-sm font-semibold whitespace-nowrap" > - Be Citizen + {t('nav.beCitizen')}
@@ -129,7 +129,7 @@ const AppLayout: React.FC = () => { href="/docs" className="text-gray-300 hover:text-white transition-colors text-sm" > - Docs + {t('nav.docs')}
@@ -161,7 +161,7 @@ const AppLayout: React.FC = () => { className="flex flex-col items-center gap-0.5 sm:gap-1 p-1.5 sm:p-2 rounded-xl bg-gray-900/70 border border-green-500/40 text-[10px] sm:text-xs font-medium transition-all hover:scale-[1.03] active:scale-95 cursor-pointer text-gray-300 hover:text-white" > - Dashboard + {t('nav.dashboard')} {/* Wallet */} {/* Be Citizen */} {/* Governance (dropdown) */}
@@ -186,24 +186,24 @@ const AppLayout: React.FC = () => { className="w-full flex flex-col items-center gap-0.5 sm:gap-1 p-1.5 sm:p-2 rounded-xl bg-gray-900/70 border border-green-500/40 text-[10px] sm:text-xs font-medium transition-all hover:scale-[1.03] active:scale-95 cursor-pointer text-gray-300 hover:text-white" > - Governance + {t('nav.governance')} {openMenu === 'governance' && (
)} @@ -215,24 +215,24 @@ const AppLayout: React.FC = () => { className="w-full flex flex-col items-center gap-0.5 sm:gap-1 p-1.5 sm:p-2 rounded-xl bg-gray-900/70 border border-red-500/40 text-[10px] sm:text-xs font-medium transition-all hover:scale-[1.03] active:scale-95 cursor-pointer text-gray-300 hover:text-white" > - Trading + {t('nav.trading')} {openMenu === 'trading' && (
)} @@ -243,7 +243,7 @@ const AppLayout: React.FC = () => { className="flex flex-col items-center gap-0.5 sm:gap-1 p-1.5 sm:p-2 rounded-xl bg-gray-900/70 border border-yellow-400/40 text-[10px] sm:text-xs font-medium transition-all hover:scale-[1.03] active:scale-95 cursor-pointer text-gray-300 hover:text-white" > - Education + {t('nav.education')} {/* Settings */} {/* Logout */}
@@ -281,81 +281,81 @@ const AppLayout: React.FC = () => { className="w-full text-left px-4 py-3 rounded-lg text-gray-300 hover:bg-gray-800 hover:text-white flex items-center gap-3" > - Dashboard + {t('nav.dashboard')}
-
Governance
+
{t('nav.governance')}
-
Trading
+
{t('nav.trading')}
) : ( @@ -380,14 +380,14 @@ const AppLayout: React.FC = () => { className="w-full text-left px-4 py-3 rounded-lg text-cyan-300 hover:bg-gray-800 flex items-center gap-3" > - Be Citizen + {t('nav.beCitizen')} )} @@ -398,7 +398,7 @@ const AppLayout: React.FC = () => { className="w-full text-left px-4 py-3 rounded-lg text-gray-300 hover:bg-gray-800 hover:text-white flex items-center gap-3" > - Docs + {t('nav.docs')}
@@ -499,10 +499,10 @@ const AppLayout: React.FC = () => {

- Staking Rewards + {t('staking.title')}

- Stake your tokens and earn rewards + {t('staking.subtitle')}

@@ -513,10 +513,10 @@ const AppLayout: React.FC = () => {

- Multi-Signature Wallet + {t('multiSig.title')}

- Secure your funds with multi-signature protection + {t('multiSig.subtitle')}

@@ -571,7 +571,7 @@ const AppLayout: React.FC = () => { }} className="bg-green-600 hover:bg-green-700 text-white px-6 py-3 rounded-full shadow-lg flex items-center gap-2 transition-all" > - ← Back to Home + {`← ${t('common.backToHome')}`}
)} @@ -600,12 +600,12 @@ const AppLayout: React.FC = () => { PezkuwiChain

- Decentralized governance platform + {t('footer.platform')}

-

Developers

+

{t('footer.developers')}

- +

- © 2024 PezkuwiChain. All rights reserved. + {t('footer.copyright')}

diff --git a/web/src/components/ErrorBoundary.tsx b/web/src/components/ErrorBoundary.tsx index 9ad5fabe..ec668e84 100644 --- a/web/src/components/ErrorBoundary.tsx +++ b/web/src/components/ErrorBoundary.tsx @@ -4,6 +4,7 @@ // Catches React errors and displays fallback UI import React, { Component, ErrorInfo, ReactNode } from 'react'; +import { useTranslation, Translation } from 'react-i18next'; import { Card, CardContent } from '@/components/ui/card'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Button } from '@/components/ui/button'; @@ -92,31 +93,33 @@ export class ErrorBoundary extends Component { // Default error UI return ( + + {(t) => (
-

Something Went Wrong

+

{t('errors.somethingWentWrong')}

- An unexpected error occurred. We apologize for the inconvenience. + {t('errors.unexpectedError')}

{this.state.error && (
- Error Details (for developers) + {t('errors.errorDetails')}
- Error: + {t('errors.error')}
                             {this.state.error.toString()}
                           
{this.state.errorInfo && (
- Component Stack: + {t('errors.componentStack')}
                               {this.state.errorInfo.componentStack}
                             
@@ -134,7 +137,7 @@ export class ErrorBoundary extends Component { className="bg-green-600 hover:bg-green-700 flex items-center gap-2" > - Try Again + {t('common.tryAgain')}

- If this problem persists, please contact support at{' '} + {t('errors.contactSupport')}{' '} {

+ )} + ); } @@ -187,6 +192,7 @@ export const RouteErrorBoundary: React.FC<{ routeName?: string; }> = ({ children, routeName = 'this page' }) => { const [hasError, setHasError] = React.useState(false); + const { t } = useTranslation(); const handleReset = () => { setHasError(false); @@ -198,12 +204,12 @@ export const RouteErrorBoundary: React.FC<{ - Error loading {routeName} - An error occurred while rendering this component. + {t('errors.errorLoading', { routeName })} + {t('errors.renderError')}
@@ -223,17 +229,18 @@ const RouteErrorFallback: React.FC<{ routeName: string; onReset: () => void }> = routeName, onReset, }) => { + const { t } = useTranslation(); return (
- Error loading {routeName} - An unexpected error occurred. + {t('errors.errorLoading', { routeName })} + {t('errors.unexpectedError')}
diff --git a/web/src/components/GovernanceInterface.tsx b/web/src/components/GovernanceInterface.tsx index a290610d..c1067a28 100644 --- a/web/src/components/GovernanceInterface.tsx +++ b/web/src/components/GovernanceInterface.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { TrendingUp, FileText, Users, Shield, Vote, History } from 'lucide-react'; import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs'; import GovernanceOverview from './governance/GovernanceOverview'; @@ -9,6 +10,7 @@ import MyVotes from './governance/MyVotes'; import GovernanceHistory from './governance/GovernanceHistory'; const GovernanceInterface: React.FC = () => { + const { t } = useTranslation(); const [activeTab, setActiveTab] = useState('overview'); return ( @@ -17,11 +19,11 @@ const GovernanceInterface: React.FC = () => {

- On-Chain Governance + {t('governance.title')}

- Participate in PezkuwiChain's decentralized governance. Vote on proposals, elect representatives, and shape the future of the network. + {t('governance.description')}

@@ -29,27 +31,27 @@ const GovernanceInterface: React.FC = () => { - Overview + {t('governance.overview')} - Proposals + {t('governance.proposals')} - Elections + {t('governance.elections')} - Delegation + {t('governance.delegation')} - My Votes + {t('governance.myVotes')} - History + {t('governance.history')} diff --git a/web/src/components/LPStakeModal.tsx b/web/src/components/LPStakeModal.tsx index 0b2af4f4..bd671b1a 100644 --- a/web/src/components/LPStakeModal.tsx +++ b/web/src/components/LPStakeModal.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { X, Lock, AlertCircle, Loader2, Clock } from 'lucide-react'; import { web3FromAddress } from '@pezkuwi/extension-dapp'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; @@ -36,10 +37,10 @@ interface DurationOption { } const DURATION_OPTIONS: DurationOption[] = [ - { label: '1 Ay', months: 1, multiplier: 1 }, - { label: '3 Ay', months: 3, multiplier: 1.5 }, - { label: '6 Ay', months: 6, multiplier: 2 }, - { label: '1 Yıl', months: 12, multiplier: 3 }, + { label: 'lpStake.month1', months: 1, multiplier: 1 }, + { label: 'lpStake.month3', months: 3, multiplier: 1.5 }, + { label: 'lpStake.month6', months: 6, multiplier: 2 }, + { label: 'lpStake.year1', months: 12, multiplier: 3 }, ]; export const LPStakeModal: React.FC = ({ @@ -48,6 +49,7 @@ export const LPStakeModal: React.FC = ({ lpToken, onStakeSuccess, }) => { + const { t } = useTranslation(); const { assetHubApi, selectedAccount, isAssetHubReady } = usePezkuwi(); const [isProcessing, setIsProcessing] = useState(false); const [error, setError] = useState(null); @@ -62,18 +64,18 @@ export const LPStakeModal: React.FC = ({ const handleStake = async () => { if (!assetHubApi || !isAssetHubReady || !selectedAccount || poolId === undefined) { - setError('API bağlantısı hazır değil'); + setError(t('lpStake.apiNotReady')); return; } const amount = parseFloat(stakeAmount); if (isNaN(amount) || amount <= 0) { - setError('Geçerli bir miktar girin'); + setError(t('lpStake.invalidAmount')); return; } if (amount > maxBalance) { - setError('Yetersiz LP token bakiyesi'); + setError(t('lpStake.insufficientBalance')); return; } @@ -108,8 +110,9 @@ export const LPStakeModal: React.FC = ({ ); }); - const durationLabel = DURATION_OPTIONS.find(d => d.months === selectedDuration)?.label || `${selectedDuration} ay`; - setSuccess(`${stakeAmount} ${lpToken.symbol} başarıyla ${durationLabel} süreyle stake edildi!`); + const durationOption = DURATION_OPTIONS.find(d => d.months === selectedDuration); + const durationLabel = durationOption ? t(durationOption.label) : `${selectedDuration}`; + setSuccess(t('lpStake.success', { amount: stakeAmount, symbol: lpToken.symbol, duration: durationLabel })); setStakeAmount(''); if (onStakeSuccess) { @@ -121,7 +124,7 @@ export const LPStakeModal: React.FC = ({ onClose(); }, 2000); } catch (err) { - setError(err instanceof Error ? err.message : 'Stake işlemi başarısız oldu'); + setError(err instanceof Error ? err.message : t('lpStake.failed')); } finally { setIsProcessing(false); } @@ -142,7 +145,7 @@ export const LPStakeModal: React.FC = ({
-

LP Token Stake

+

{t('lpStake.title')}

{lpToken.symbol}

@@ -169,7 +172,7 @@ export const LPStakeModal: React.FC = ({
{DURATION_OPTIONS.map((option) => ( @@ -183,7 +186,7 @@ export const LPStakeModal: React.FC = ({ }`} disabled={isProcessing} > -
{option.label}
+
{t(option.label)}
{option.multiplier}x
))} @@ -193,12 +196,12 @@ export const LPStakeModal: React.FC = ({ {/* Balance Info */}
- Mevcut Bakiye: + {t('lpStake.currentBalance')} {lpToken.balance} {lpToken.symbol}
{selectedDurationOption && (
- Ödül Çarpanı: + {t('lpStake.rewardMultiplier')} {selectedDurationOption.multiplier}x
)} @@ -207,7 +210,7 @@ export const LPStakeModal: React.FC = ({ {/* Amount Input */}
= ({
- Pool ID: {poolId} + {t('lpStake.poolId', { id: poolId })}
@@ -239,8 +242,7 @@ export const LPStakeModal: React.FC = ({
- LP tokenlarınız seçilen süre boyunca kilitlenecektir. Bu süre içinde unstake yapamazsınız. - Ödüller her blokta otomatik olarak birikir. + {t('lpStake.lockWarning')}
@@ -254,12 +256,12 @@ export const LPStakeModal: React.FC = ({ {isProcessing ? ( <> - Stake Ediliyor... + {t('lpStake.staking')} ) : ( <> - {selectedDurationOption?.label} Stake Et + {t('lpStake.stakeBtn', { duration: selectedDurationOption ? t(selectedDurationOption.label) : '' })} )} diff --git a/web/src/components/LPStakingModal.tsx b/web/src/components/LPStakingModal.tsx index 165b0526..e95d88ad 100644 --- a/web/src/components/LPStakingModal.tsx +++ b/web/src/components/LPStakingModal.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { X, Lock, Unlock, Gift, AlertCircle, Loader2, Info } from 'lucide-react'; import { web3FromAddress } from '@pezkuwi/extension-dapp'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; @@ -31,6 +32,7 @@ const LP_TOKEN_NAMES: Record = { export const LPStakingModal: React.FC = ({ isOpen, onClose }) => { const { assetHubApi, selectedAccount, isAssetHubReady } = usePezkuwi(); + const { t } = useTranslation(); const [pools, setPools] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isProcessing, setIsProcessing] = useState(false); @@ -114,7 +116,7 @@ export const LPStakingModal: React.FC = ({ isOpen, onClose } } catch (err) { console.error('Error fetching staking pools:', err); - setError('Failed to fetch staking pools'); + setError(t('lpStaking.fetchError')); } finally { setIsLoading(false); } @@ -165,10 +167,10 @@ export const LPStakingModal: React.FC = ({ isOpen, onClose ); }); - setSuccess(`Successfully staked ${stakeAmount} ${pool.stakedAsset}!`); + setSuccess(t('lpStaking.stakeSuccess', { amount: stakeAmount, asset: pool.stakedAsset })); setStakeAmount(''); } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to stake'); + setError(err instanceof Error ? err.message : t('lpStaking.stakeFailed')); } finally { setIsProcessing(false); } @@ -211,10 +213,10 @@ export const LPStakingModal: React.FC = ({ isOpen, onClose ); }); - setSuccess(`Successfully unstaked ${unstakeAmount} ${pool.stakedAsset}!`); + setSuccess(t('lpStaking.unstakeSuccess', { amount: unstakeAmount, asset: pool.stakedAsset })); setUnstakeAmount(''); } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to unstake'); + setError(err instanceof Error ? err.message : t('lpStaking.unstakeFailed')); } finally { setIsProcessing(false); } @@ -252,9 +254,9 @@ export const LPStakingModal: React.FC = ({ isOpen, onClose ); }); - setSuccess('Successfully harvested rewards!'); + setSuccess(t('lpStaking.harvestSuccess')); } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to harvest rewards'); + setError(err instanceof Error ? err.message : t('lpStaking.harvestFailed')); } finally { setIsProcessing(false); } @@ -268,7 +270,7 @@ export const LPStakingModal: React.FC = ({ isOpen, onClose
-

LP Staking

+

{t('lpStaking.title')}

@@ -290,17 +292,17 @@ export const LPStakingModal: React.FC = ({ isOpen, onClose {isLoading ? (
-

Loading staking pools...

+

{t('lpStaking.loading')}

) : pools.length === 0 ? ( - No staking pools available. + {t('lpStaking.noPools')} ) : ( <>
- + = ({ isOpen, onClose className="w-full bg-gradient-to-r from-green-600 to-cyan-600 h-12" > {isProcessing ? : } - {isProcessing ? 'Staking...' : 'Stake LP Tokens'} + {isProcessing ? t('lpStaking.staking') : t('lpStaking.stakeLp')}
@@ -386,7 +388,7 @@ export const LPStakingModal: React.FC = ({ isOpen, onClose
- +
= ({ isOpen, onClose className="w-full bg-gradient-to-r from-orange-600 to-red-600 h-12" > {isProcessing ? : } - {isProcessing ? 'Unstaking...' : 'Unstake LP Tokens'} + {isProcessing ? t('lpStaking.unstaking') : t('lpStaking.unstakeLp')}
@@ -418,7 +420,7 @@ export const LPStakingModal: React.FC = ({ isOpen, onClose
-

Pending Rewards

+

{t('lpStaking.pendingRewards')}

{currentPool ? formatAmount(currentPool.pendingRewards) : '0'} PEZ

@@ -429,7 +431,7 @@ export const LPStakingModal: React.FC = ({ isOpen, onClose className="w-full bg-gradient-to-r from-purple-600 to-pink-600 h-12" > {isProcessing ? : } - {isProcessing ? 'Harvesting...' : 'Harvest Rewards'} + {isProcessing ? t('lpStaking.harvesting') : t('lpStaking.harvestRewards')}
diff --git a/web/src/components/Layout.tsx b/web/src/components/Layout.tsx index e4745b31..1c78397e 100644 --- a/web/src/components/Layout.tsx +++ b/web/src/components/Layout.tsx @@ -1,15 +1,17 @@ import React from 'react'; import { Link, NavLink } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; const PezkuwiChainLogo: React.FC = () => { return ( - PezkuwiChain Logo + PezkuwiChain ); }; const Header: React.FC = () => { const linkStyle = "text-white hover:text-green-400 transition-colors"; const activeLinkStyle = { color: '#34D399' }; // green-400 + const { t } = useTranslation(); return (
@@ -19,16 +21,16 @@ const Header: React.FC = () => {
@@ -37,10 +39,11 @@ const Header: React.FC = () => { }; const Footer: React.FC = () => { + const { t } = useTranslation(); return (
-

© {new Date().getFullYear()} PezkuwiChain. All rights reserved.

+

© {new Date().getFullYear()} PezkuwiChain. {t('footer.rights')}

); diff --git a/web/src/components/MultisigMembers.tsx b/web/src/components/MultisigMembers.tsx index 141f6b82..09eaf5e1 100644 --- a/web/src/components/MultisigMembers.tsx +++ b/web/src/components/MultisigMembers.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { Shield, Users, CheckCircle, XCircle, ExternalLink } from 'lucide-react'; import { Card } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; @@ -31,6 +32,7 @@ export const MultisigMembers: React.FC = ({ specificAddresses = {}, showMultisigAddress = true, }) => { + const { t } = useTranslation(); const { api, isApiReady } = usePezkuwi(); const [members, setMembers] = useState([]); const [multisigAddress, setMultisigAddress] = useState(''); @@ -78,29 +80,29 @@ export const MultisigMembers: React.FC = ({
-

USDT Treasury Multisig

+

{t('multisigMembers.title')}

- {USDT_MULTISIG_CONFIG.threshold}/{members.length} Signatures Required + {t('multisigMembers.threshold', { threshold: USDT_MULTISIG_CONFIG.threshold, total: members.length })}

- {members.length} Members + {t('multisigMembers.members', { count: members.length })}
{/* Multisig Address */} {showMultisigAddress && multisigAddress && (
-

Multisig Account

+

{t('multisigMembers.account')}

{formatMultisigAddress(multisigAddress)}
@@ -126,7 +128,7 @@ export const MultisigMembers: React.FC = ({ {member.isUnique && ( - On-Chain + {t('multisigMembers.onChain')} )}
@@ -139,9 +141,9 @@ export const MultisigMembers: React.FC = ({
{member.isUnique ? ( - + ) : ( - + )}
@@ -153,12 +155,12 @@ export const MultisigMembers: React.FC = ({ -

Security Features

+

{t('multisigMembers.securityTitle')}

    -
  • • {USDT_MULTISIG_CONFIG.threshold} out of {members.length} signatures required
  • -
  • • {members.filter(m => m.isUnique).length} members verified on-chain via Tiki
  • -
  • • No single person can control funds
  • -
  • • All transactions visible on blockchain
  • +
  • • {t('multisigMembers.sigRequired', { threshold: USDT_MULTISIG_CONFIG.threshold, total: members.length })}
  • +
  • • {t('multisigMembers.verifiedOnChain', { count: members.filter(m => m.isUnique).length })}
  • +
  • • {t('multisigMembers.noSingleControl')}
  • +
  • • {t('multisigMembers.allVisible')}
@@ -172,7 +174,7 @@ export const MultisigMembers: React.FC = ({ rel="noopener noreferrer" className="inline-flex items-center gap-2 text-sm text-blue-400 hover:text-blue-300" > - View on Explorer + {t('multisigMembers.viewExplorer')}
diff --git a/web/src/components/NetworkStats.tsx b/web/src/components/NetworkStats.tsx index d13987ce..c6a706d6 100644 --- a/web/src/components/NetworkStats.tsx +++ b/web/src/components/NetworkStats.tsx @@ -1,10 +1,12 @@ import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Activity, Wifi, WifiOff, Users, Box, TrendingUp } from 'lucide-react'; export const NetworkStats: React.FC = () => { + const { t } = useTranslation(); const { api, assetHubApi, peopleApi, isApiReady, isAssetHubReady, isPeopleReady, error } = usePezkuwi(); const [blockNumber, setBlockNumber] = useState(0); const [blockHash, setBlockHash] = useState(''); @@ -125,13 +127,13 @@ export const NetworkStats: React.FC = () => { - Network Disconnected + {t('networkStats.disconnected')}

{error}

- Make sure your validator node is running at ws://127.0.0.1:9944 + {t('networkStats.disconnectedDesc')}

@@ -144,7 +146,7 @@ export const NetworkStats: React.FC = () => { - Connecting to Network... + {t('networkStats.connecting')} @@ -158,15 +160,15 @@ export const NetworkStats: React.FC = () => { - Network Status + {t('networkStats.title')}
- Connected + {t('networkStats.connected')} - {peers} peers + {peers} {t('networkStats.peers')}
@@ -176,7 +178,7 @@ export const NetworkStats: React.FC = () => { - Latest Block + {t('networkStats.latestBlock')} @@ -196,7 +198,7 @@ export const NetworkStats: React.FC = () => { - Finalized Block + {t('networkStats.finalizedBlock')} @@ -204,7 +206,7 @@ export const NetworkStats: React.FC = () => { #{finalizedBlock.toLocaleString()}
- {blockNumber - finalizedBlock} blocks behind + {blockNumber - finalizedBlock} {t('networkStats.blocksBehind')}
@@ -214,7 +216,7 @@ export const NetworkStats: React.FC = () => { - Active Validators + {t('networkStats.activeValidators')} @@ -222,7 +224,7 @@ export const NetworkStats: React.FC = () => { {validatorCount}
- Validating blocks + {t('networkStats.validating')}
@@ -232,7 +234,7 @@ export const NetworkStats: React.FC = () => { - Active Collators + {t('networkStats.activeCollators')} @@ -240,7 +242,7 @@ export const NetworkStats: React.FC = () => { {collatorCount}
- Producing blocks + {t('networkStats.producing')}
@@ -250,7 +252,7 @@ export const NetworkStats: React.FC = () => { - Active Nominators + {t('networkStats.activeNominators')} @@ -258,7 +260,7 @@ export const NetworkStats: React.FC = () => { {nominatorCount}
- Staking to validators + {t('networkStats.staking')}
diff --git a/web/src/components/NftList.tsx b/web/src/components/NftList.tsx index 7394ae4f..e34084f0 100644 --- a/web/src/components/NftList.tsx +++ b/web/src/components/NftList.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Loader2, Award, Crown, Shield, Users } from 'lucide-react'; @@ -39,6 +40,7 @@ const getRoleBadgeColor = (role: string) => { }; export const NftList: React.FC = () => { + const { t } = useTranslation(); const { api, isApiReady, selectedAccount } = usePezkuwi(); const [tikis, setTikis] = useState([]); const [loading, setLoading] = useState(true); @@ -72,8 +74,8 @@ export const NftList: React.FC = () => { return ( - Your NFTs (Tikis) - Your Tiki collection + {t('nftList.title')} + {t('nftList.description')}
@@ -88,12 +90,12 @@ export const NftList: React.FC = () => { return ( - Your NFTs (Tikis) - Your Tiki collection + {t('nftList.title')} + {t('nftList.description')}
-

{error}

+

{t('nftList.error')}

@@ -104,15 +106,15 @@ export const NftList: React.FC = () => { return ( - Your NFTs (Tikis) - Your Tiki collection + {t('nftList.title')} + {t('nftList.description')}
-

No NFTs yet

+

{t('nftList.empty')}

- Complete your citizenship application to receive your Welati Tiki NFT + {t('nftList.emptyHelp')}

@@ -125,9 +127,9 @@ export const NftList: React.FC = () => { - Your NFTs (Tikiler) + {t('nftList.title')} - Your Tiki collection ({tikis.length} total) + {t('nftList.descriptionCount', { count: tikis.length })}
@@ -146,7 +148,7 @@ export const NftList: React.FC = () => {

- Tiki #{tiki.id} + {t('nftList.cardTitle', { id: tiki.id })}

{tiki.role} diff --git a/web/src/components/PalletsGrid.tsx b/web/src/components/PalletsGrid.tsx index 2970d73d..ac6e57d7 100644 --- a/web/src/components/PalletsGrid.tsx +++ b/web/src/components/PalletsGrid.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { Code, Database, TrendingUp, Gift, Award } from 'lucide-react'; interface Pallet { @@ -51,6 +52,7 @@ const pallets: Pallet[] = [ ]; const PalletsGrid: React.FC = () => { + const { t } = useTranslation(); const [selectedPallet, setSelectedPallet] = useState(null); return ( @@ -58,10 +60,10 @@ const PalletsGrid: React.FC = () => {

- Core Runtime Pallets + {t('palletsGrid.title')}

- Modular blockchain components powering PezkuwiChain's advanced features + {t('palletsGrid.description')}

@@ -93,10 +95,10 @@ const PalletsGrid: React.FC = () => {
- {pallet.extrinsics.length} Extrinsics + {t('palletsGrid.extrinsics', { count: pallet.extrinsics.length })} - {pallet.storage.length} Storage Items + {t('palletsGrid.storageItems', { count: pallet.storage.length })}
@@ -130,7 +132,7 @@ const PalletsGrid: React.FC = () => {
-

Extrinsics

+

{t('palletsGrid.extrinsicsTitle')}

{selectedPallet.extrinsics.map((ext) => (
@@ -142,7 +144,7 @@ const PalletsGrid: React.FC = () => {
-

Storage Items

+

{t('palletsGrid.storageTitle')}

{selectedPallet.storage.map((item) => (
diff --git a/web/src/components/PezkuwiWalletButton.tsx b/web/src/components/PezkuwiWalletButton.tsx index 157b669e..dda62b13 100644 --- a/web/src/components/PezkuwiWalletButton.tsx +++ b/web/src/components/PezkuwiWalletButton.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; @@ -23,6 +24,7 @@ export const PezkuwiWalletButton: React.FC = () => { error } = usePezkuwi(); + const { t } = useTranslation(); const [isOpen, setIsOpen] = useState(false); const { toast } = useToast(); const isMobile = useIsMobile(); @@ -38,7 +40,7 @@ export const PezkuwiWalletButton: React.FC = () => { setSelectedAccount(account); setIsOpen(false); toast({ - title: "Account Connected", + title: t('pezWallet.connected'), description: `${account.meta.name} - ${formatAddress(account.address)}`, }); }; @@ -46,8 +48,8 @@ export const PezkuwiWalletButton: React.FC = () => { const handleDisconnect = () => { disconnectWallet(); toast({ - title: "Wallet Disconnected", - description: "Your wallet has been disconnected", + title: t('pezWallet.disconnected'), + description: t('pezWallet.disconnectedDesc'), }); }; @@ -59,8 +61,8 @@ export const PezkuwiWalletButton: React.FC = () => { if (selectedAccount) { navigator.clipboard.writeText(selectedAccount.address); toast({ - title: "Address Copied", - description: "Address copied to clipboard", + title: t('pezWallet.addressCopied'), + description: t('pezWallet.addressCopiedDesc'), }); } }; @@ -96,22 +98,22 @@ export const PezkuwiWalletButton: React.FC = () => { - Account Details + {t('pezWallet.accountDetails')} - Your connected Pezkuwi account + {t('pezWallet.accountDetailsDesc')}
-
Account Name
+
{t('pezWallet.accountName')}
- {selectedAccount.meta.name || 'Unnamed Account'} + {selectedAccount.meta.name || t('pezWallet.unnamed')}
-
Address
+
{t('pezWallet.address')}
{selectedAccount.address} @@ -128,7 +130,7 @@ export const PezkuwiWalletButton: React.FC = () => {
-
Source
+
{t('pezWallet.source')}
{selectedAccount.meta.source || 'pezkuwi'}
@@ -136,7 +138,7 @@ export const PezkuwiWalletButton: React.FC = () => { {accounts.length > 1 && (
-
Switch Account
+
{t('pezWallet.switchAccount')}
{accounts.map((account) => ( {error && error.includes('not found') && ( {}}> - Install Pezkuwi Wallet Extension + {t('pezWallet.installTitle')} - You need the Pezkuwi Wallet browser extension to connect + {t('pezWallet.installDesc')}

- The Pezkuwi Wallet extension allows you to manage your accounts and sign transactions securely. + {t('pezWallet.installText')}

@@ -206,13 +208,13 @@ export const PezkuwiWalletButton: React.FC = () => { >

- After installing, refresh this page and click "Connect Wallet" again. + {t('pezWallet.installRefresh')}

@@ -222,9 +224,9 @@ export const PezkuwiWalletButton: React.FC = () => { 0} onOpenChange={setIsOpen}> - Select Account + {t('pezWallet.selectTitle')} - Choose an account to connect + {t('pezWallet.selectDesc')} @@ -236,7 +238,7 @@ export const PezkuwiWalletButton: React.FC = () => { className="w-full p-4 rounded-lg border border-gray-700 bg-gray-800/50 hover:border-green-500/50 hover:bg-gray-800 transition-all text-left" >
- {account.meta.name || 'Unnamed Account'} + {account.meta.name || t('pezWallet.unnamed')}
{account.address} diff --git a/web/src/components/PoolDashboard.tsx b/web/src/components/PoolDashboard.tsx index e3d5183a..65a95e55 100644 --- a/web/src/components/PoolDashboard.tsx +++ b/web/src/components/PoolDashboard.tsx @@ -12,6 +12,7 @@ import { NATIVE_TOKEN_ID } from '@/types/dex'; import { AddLiquidityModal } from '@/components/AddLiquidityModal'; import { RemoveLiquidityModal } from '@/components/RemoveLiquidityModal'; import { LPStakingModal } from '@/components/LPStakingModal'; +import { useTranslation } from 'react-i18next'; // Helper function to convert asset IDs to user-friendly display names // Users should only see HEZ, PEZ, USDT - wrapped tokens are backend details @@ -45,6 +46,7 @@ interface LPPosition { const PoolDashboard = () => { // Use Asset Hub API for DEX operations (assetConversion pallet is on Asset Hub) const { assetHubApi, isAssetHubReady, selectedAccount } = usePezkuwi(); + const { t } = useTranslation(); const [poolData, setPoolData] = useState(null); const [lpPosition, setLPPosition] = useState(null); @@ -331,7 +333,7 @@ const PoolDashboard = () => {
-

Loading pool data...

+

{t('poolDash.loadingPoolData')}

); @@ -350,7 +352,7 @@ const PoolDashboard = () => { return ( - No pool data available + {t('poolDash.noPoolData')} ); } @@ -365,10 +367,10 @@ const PoolDashboard = () => {
-

Pool Dashboards

+

{t('poolDash.poolDashboards')}

{
-
Epoch Start Block
+
{t('rewardDist.epochStartBlock')}
#{((currentEpoch - 1) * 432000).toLocaleString()}
-
Claim Deadline Block
+
{t('rewardDist.claimDeadline')}
#{((currentEpoch * 432000) + 100800).toLocaleString()}
@@ -116,7 +118,7 @@ const RewardDistribution: React.FC = () => {
-

Epoch Pool

+

{t('rewardDist.epochPool')}

@@ -125,11 +127,11 @@ const RewardDistribution: React.FC = () => {
- Trust Score Pool + {t('rewardDist.trustScorePool')} 90%
- Parliamentary NFTs + {t('rewardDist.parliamentaryNfts')} 10%
@@ -138,23 +140,23 @@ const RewardDistribution: React.FC = () => {
-

NFT Rewards

+

{t('rewardDist.nftRewards')}

- Total NFTs + {t('rewardDist.totalNfts')} 201
- Per NFT Reward + {t('rewardDist.perNftReward')} {Math.floor(nftRewardPerHolder).toLocaleString()} PEZ
-
Auto-distributed
-
No claim required
+
{t('rewardDist.autoDistributed')}
+
{t('rewardDist.noClaimRequired')}
@@ -165,12 +167,12 @@ const RewardDistribution: React.FC = () => {

- Reward Calculator + {t('rewardDist.rewardCalculator')}

- + {
- + {
- + {
-
Reward per Trust Point
+
{t('rewardDist.rewardPerPoint')}
{rewardPerTrustPoint.toFixed(4)} PEZ
-
Your Share
+
{t('rewardDist.yourShare')}
{((trustScoreInput / totalTrustScore) * 100).toFixed(3)}%
-
Estimated Reward
+
{t('rewardDist.estimatedReward')}
{Math.floor(userReward).toLocaleString()} PEZ
diff --git a/web/src/components/RouteGuards.tsx b/web/src/components/RouteGuards.tsx index 32a2bc42..c7118b27 100644 --- a/web/src/components/RouteGuards.tsx +++ b/web/src/components/RouteGuards.tsx @@ -5,6 +5,7 @@ import React, { useEffect, useState, ReactNode } from 'react'; import { Navigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { useAuth } from '@/contexts/AuthContext'; import { @@ -28,12 +29,152 @@ interface RouteGuardProps { // ======================================== const LoadingGuard: React.FC = () => { + const { t } = useTranslation(); return (
-

Checking permissions...

+

{t('guards.checkingPermissions')}

+
+
+
+ ); +}; + +const WalletNotConnectedGuard: React.FC = () => { + const { t } = useTranslation(); + return ( +
+ + +
+ +

{t('guards.walletNotConnected')}

+

+ {t('guards.connectWalletMessage')} +

+ +
+
+
+
+ ); +}; + +const ValidatorRequiredGuard: React.FC<{ fallbackPath: string }> = ({ fallbackPath }) => { + const { t } = useTranslation(); + return ( +
+ + + + + + {t('guards.validatorRequired')} + {t('guards.validatorMessage')} +
+ +
+
+
+
+
+
+ ); +}; + +const EducatorRequiredGuard: React.FC<{ fallbackPath: string }> = ({ fallbackPath }) => { + const { t } = useTranslation(); + return ( +
+ + + + + + {t('guards.educatorRequired')} + {t('guards.educatorMessage')} +
    +
  • {t('guards.roles.perwerdekar')}
  • +
  • {t('guards.roles.mamoste')}
  • +
  • {t('guards.roles.wezirecand')}
  • +
  • {t('guards.roles.rewsenbir')}
  • +
+
+ +
+
+
+
+
+
+ ); +}; + +const ModeratorRequiredGuard: React.FC<{ fallbackPath: string }> = ({ fallbackPath }) => { + const { t } = useTranslation(); + return ( +
+ + + + + + {t('guards.moderatorRequired')} + {t('guards.moderatorMessage')} +
+ +
+
+
+
+
+
+ ); +}; + +const AdminRequiredGuard: React.FC<{ fallbackPath: string }> = ({ fallbackPath }) => { + const { t } = useTranslation(); + return ( +
+ + + + + + {t('guards.adminRequired')} + {t('guards.adminMessage')} +
+ +
+
+
@@ -98,27 +239,7 @@ export const CitizenRoute: React.FC = ({ // Not connected to wallet if (!selectedAccount) { - return ( -
- - -
- -

Wallet Not Connected

-

- Please connect your Pezkuwi wallet to access this feature. -

- -
-
-
-
- ); + return ; } // Not a citizen @@ -192,29 +313,7 @@ export const ValidatorRoute: React.FC = ({ // Not in validator pool if (isValidator === false) { - return ( -
- - - - - - Validator Access Required - You must be registered in the Validator Pool to access this feature. -
- -
-
-
-
-
-
- ); + return ; } // Authorized @@ -283,35 +382,7 @@ export const EducatorRoute: React.FC = ({ // Not an educator if (isEducator === false) { - return ( -
- - - - - - Educator Role Required - You need one of these Tiki roles to create courses: -
    -
  • Perwerdekar (Educator)
  • -
  • Mamoste (Teacher)
  • -
  • WezireCand (Education Minister)
  • -
  • Rewsenbîr (Intellectual)
  • -
-
- -
-
-
-
-
-
- ); + return ; } // Authorized @@ -380,29 +451,7 @@ export const ModeratorRoute: React.FC = ({ // Not a moderator if (isModerator === false) { - return ( -
- - - - - - Moderator Access Required - You need moderator privileges to access this feature. -
- -
-
-
-
-
-
- ); + return ; } // Authorized @@ -436,29 +485,7 @@ export const AdminRoute: React.FC = ({ // Not admin if (!isAdmin) { - return ( -
- - - - - - Admin Access Required - You do not have permission to access the admin panel. -
- -
-
-
-
-
-
- ); + return ; } // Authorized diff --git a/web/src/components/TeamSection.tsx b/web/src/components/TeamSection.tsx index e070e705..db82fe11 100644 --- a/web/src/components/TeamSection.tsx +++ b/web/src/components/TeamSection.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { useTranslation } from 'react-i18next'; import { Card, CardContent } from './ui/card'; import { Badge } from './ui/badge'; import { Users } from 'lucide-react'; @@ -11,53 +12,54 @@ interface TeamMember { } const TeamSection: React.FC = () => { + const { t } = useTranslation(); const teamMembers: TeamMember[] = [ { name: "Satoshi Qazi Muhammed", - role: "Chief Architect", - description: "Blockchain visionary and protocol designer", + role: t('teamSection.chiefArchitect'), + description: t('teamSection.chiefArchitectDesc'), image: "https://d64gsuwffb70l.cloudfront.net/68ec477a0a2fa844d6f9df15_1760358016604_9ae228b4.webp" }, { name: "Abdurrahman Qasimlo", - role: "Governance Lead", - description: "Democratic systems and consensus mechanisms", + role: t('teamSection.govLead'), + description: t('teamSection.govLeadDesc'), image: "https://d64gsuwffb70l.cloudfront.net/68ec477a0a2fa844d6f9df15_1760358018357_f19e128d.webp" }, { name: "Abdusselam Barzani", - role: "Protocol Engineer", - description: "Core protocol development and optimization", + role: t('teamSection.protocolEngineer'), + description: t('teamSection.protocolEngineerDesc'), image: "https://d64gsuwffb70l.cloudfront.net/68ec477a0a2fa844d6f9df15_1760358020150_1ea35457.webp" }, { name: "Ihsan Nuri", - role: "Security Advisor", - description: "Cryptography and network security expert", + role: t('teamSection.securityAdvisor'), + description: t('teamSection.securityAdvisorDesc'), image: "https://d64gsuwffb70l.cloudfront.net/68ec477a0a2fa844d6f9df15_1760358021872_362f1214.webp" }, { name: "Seyh Said", - role: "Community Director", - description: "Ecosystem growth and community relations", + role: t('teamSection.communityDirector'), + description: t('teamSection.communityDirectorDesc'), image: "https://d64gsuwffb70l.cloudfront.net/68ec477a0a2fa844d6f9df15_1760358023648_4bb8f4c7.webp" }, { name: "Seyyid Riza", - role: "Treasury Manager", - description: "Economic models and treasury operations", + role: t('teamSection.treasuryManager'), + description: t('teamSection.treasuryManagerDesc'), image: "https://d64gsuwffb70l.cloudfront.net/68ec477a0a2fa844d6f9df15_1760358025533_d9df77a9.webp" }, { name: "Beritan", - role: "Developer Relations", - description: "Technical documentation and developer support", + role: t('teamSection.devRelations'), + description: t('teamSection.devRelationsDesc'), image: "https://d64gsuwffb70l.cloudfront.net/68ec477a0a2fa844d6f9df15_1760358027281_9254657a.webp" }, { name: "Mashuk Xaznevi", - role: "Research Lead", - description: "Blockchain research and innovation", + role: t('teamSection.researchLead'), + description: t('teamSection.researchLeadDesc'), image: "https://d64gsuwffb70l.cloudfront.net/68ec477a0a2fa844d6f9df15_1760358029000_3ffc04bc.webp" } ]; @@ -68,13 +70,13 @@ const TeamSection: React.FC = () => {
- Our Team + {t('teamSection.title')}

- Meet the Visionaries + {t('teamSection.subtitle')}

- A dedicated team of blockchain experts and governance specialists building the future of decentralized democracy + {t('teamSection.description')}

diff --git a/web/src/components/TokenSwap.tsx b/web/src/components/TokenSwap.tsx index 2dc401db..99060542 100644 --- a/web/src/components/TokenSwap.tsx +++ b/web/src/components/TokenSwap.tsx @@ -14,6 +14,7 @@ import { formatAssetLocation, NATIVE_TOKEN_ID } from '@pezkuwi/utils/dex'; import { useToast } from '@/hooks/use-toast'; import { KurdistanSun } from './KurdistanSun'; import { PriceChart } from './trading/PriceChart'; +import { useTranslation } from 'react-i18next'; // Available tokens for swap const AVAILABLE_TOKENS = [ @@ -27,6 +28,7 @@ const TokenSwap = () => { const { assetHubApi, isAssetHubReady, selectedAccount } = usePezkuwi(); const { balances, refreshBalances } = useWallet(); const { toast } = useToast(); + const { t } = useTranslation(); const [fromToken, setFromToken] = useState('PEZ'); const [toToken, setToToken] = useState('HEZ'); @@ -514,8 +516,8 @@ const TokenSwap = () => { const handleConfirmSwap = async () => { if (!assetHubApi || !selectedAccount) { toast({ - title: 'Error', - description: 'Please connect your wallet', + title: t('common.error'), + description: t('common.connectWalletAlert'), variant: 'destructive', }); return; @@ -523,8 +525,8 @@ const TokenSwap = () => { if (!isDexAvailable) { toast({ - title: 'DEX Not Available', - description: 'AssetConversion pallet is not enabled in runtime', + title: t('tokenSwap.dexNotAvailable'), + description: t('tokenSwap.dexNotAvailableDesc'), variant: 'destructive', }); return; @@ -532,8 +534,8 @@ const TokenSwap = () => { if (!exchangeRate || exchangeRate === 0) { toast({ - title: 'Error', - description: 'No liquidity pool available for this pair', + title: t('common.error'), + description: t('swap.noPool'), variant: 'destructive', }); return; @@ -545,8 +547,8 @@ const TokenSwap = () => { if (fromAmountNum > fromBalanceNum) { toast({ - title: 'Insufficient Balance', - description: `You only have ${fromBalanceNum.toFixed(4)} ${getTokenDisplayName(fromToken)}. Cannot swap ${fromAmountNum} ${getTokenDisplayName(fromToken)}.`, + title: t('swap.insufficientBalanceBtn', { token: getTokenDisplayName(fromToken) }), + description: t('tokenSwap.insufficientBalanceToast', { balance: fromBalanceNum.toFixed(4), token: getTokenDisplayName(fromToken), amount: fromAmountNum }), variant: 'destructive', }); return; @@ -685,8 +687,8 @@ const TokenSwap = () => { if (import.meta.env.DEV) console.log('✅ Transaction in block:', status.asInBlock.toHex()); toast({ - title: 'Transaction Submitted', - description: `Processing in block ${status.asInBlock.toHex().slice(0, 10)}...`, + title: t('tokenSwap.txSubmitted'), + description: t('tokenSwap.processingInBlock', { hash: status.asInBlock.toHex().slice(0, 10) }), }); } @@ -705,7 +707,7 @@ const TokenSwap = () => { } toast({ - title: 'Error', + title: t('common.error'), description: errorMessage, variant: 'destructive', }); @@ -720,8 +722,8 @@ const TokenSwap = () => { if (hasSwapEvent || fromToken === 'HEZ' || toToken === 'HEZ') { toast({ - title: 'Success!', - description: `Swapped ${fromAmount} ${fromToken} for ~${toAmount} ${toToken}`, + title: t('common.success'), + description: t('swap.swapped', { fromAmount, fromToken, toAmount, toToken }), }); setFromAmount(''); @@ -809,8 +811,8 @@ const TokenSwap = () => { }, 3000); } else { toast({ - title: 'Error', - description: 'Swap transaction failed', + title: t('common.error'), + description: t('swap.swapFailed'), variant: 'destructive', }); } @@ -822,8 +824,8 @@ const TokenSwap = () => { } catch (error) { if (import.meta.env.DEV) console.error('Swap failed:', error); toast({ - title: 'Error', - description: error instanceof Error ? error.message : 'Swap transaction failed', + title: t('common.error'), + description: error instanceof Error ? error.message : t('swap.swapFailed'), variant: 'destructive', }); setIsSwapping(false); @@ -843,20 +845,19 @@ const TokenSwap = () => {
-

DEX Coming Soon

+

{t('tokenSwap.dexComingSoon')}

- The AssetConversion pallet is not yet enabled in the runtime. - Token swapping functionality will be available after the next runtime upgrade. + {t('tokenSwap.dexComingDesc')}

- Scheduled for Next Runtime Upgrade + {t('tokenSwap.scheduledUpgrade')}
@@ -873,7 +874,7 @@ const TokenSwap = () => {

- Processing your swap... + {t('tokenSwap.processingSwap')}

@@ -891,7 +892,7 @@ const TokenSwap = () => {
-

Token Swap

+

{t('tokenSwap.tokenSwap')}

@@ -901,7 +902,7 @@ const TokenSwap = () => { - Please connect your wallet to swap tokens + {t('tokenSwap.connectWalletAlert')} )} @@ -909,9 +910,9 @@ const TokenSwap = () => {
- From + {t('tokenSwap.from')} - Balance: {fromBalance} {getTokenDisplayName(fromToken)} + {t('common.balance')}: {fromBalance} {getTokenDisplayName(fromToken)}
@@ -980,9 +981,9 @@ const TokenSwap = () => {
- To + {t('tokenSwap.to')} - Balance: {toBalance} {getTokenDisplayName(toToken)} + {t('common.balance')}: {toBalance} {getTokenDisplayName(toToken)}
@@ -1041,15 +1042,15 @@ const TokenSwap = () => {
- Exchange Rate + {t('common.exchangeRate')} {isLoadingRate ? ( - 'Loading...' + t('common.loading') ) : exchangeRate > 0 ? ( `1 ${getTokenDisplayName(fromToken)} = ${exchangeRate.toFixed(4)} ${getTokenDisplayName(toToken)}` ) : ( - 'No pool available' + t('tokenSwap.noPoolAvailable') )}
@@ -1063,7 +1064,7 @@ const TokenSwap = () => { priceImpact < 5 ? 'text-yellow-500' : 'text-red-500' }`} /> - Price Impact + {t('tokenSwap.priceImpact')} { {/* LP Fee */} {fromAmount && parseFloat(fromAmount) > 0 && lpFee && (
- Liquidity Provider Fee + {t('tokenSwap.lpFee')} {lpFee} {getTokenDisplayName(fromToken)}
)} @@ -1086,13 +1087,13 @@ const TokenSwap = () => { {/* Minimum Received */} {fromAmount && parseFloat(fromAmount) > 0 && minimumReceived && (
- Minimum Received + {t('tokenSwap.minimumReceived')} {minimumReceived} {getTokenDisplayName(toToken)}
)}
- Slippage Tolerance + {t('common.slippageTolerance')} {slippage}%
@@ -1102,7 +1103,7 @@ const TokenSwap = () => { - Insufficient {getTokenDisplayName(fromToken)} balance. You have {fromBalance} {getTokenDisplayName(fromToken)} but trying to swap {fromAmount} {getTokenDisplayName(fromToken)}. + {t('tokenSwap.insufficientWarning', { token: getTokenDisplayName(fromToken), balance: fromBalance, amount: fromAmount })} )} @@ -1112,7 +1113,7 @@ const TokenSwap = () => { - High price impact! Your trade will significantly affect the pool price. Consider a smaller amount or check if there's better liquidity. + {t('tokenSwap.highPriceImpact')} )} @@ -1123,12 +1124,12 @@ const TokenSwap = () => { disabled={!fromAmount || parseFloat(fromAmount) <= 0 || !selectedAccount || exchangeRate === 0 || hasInsufficientBalance} > {!selectedAccount - ? 'Connect Wallet' + ? t('tokenSwap.connectWallet') : hasInsufficientBalance - ? `Insufficient ${getTokenDisplayName(fromToken)} Balance` + ? t('tokenSwap.insufficientBalance', { token: getTokenDisplayName(fromToken) }) : exchangeRate === 0 - ? 'No Pool Available' - : 'Swap Tokens'} + ? t('tokenSwap.noPool') + : t('tokenSwap.swapTokens')}
@@ -1136,11 +1137,11 @@ const TokenSwap = () => {

- Liquidity Pools + {t('tokenSwap.liquidityPools')}

{isLoadingPools ? ( -
Loading pools...
+
{t('tokenSwap.loadingPools')}
) : liquidityPools.length > 0 ? (
{liquidityPools.map((pool, idx) => ( @@ -1158,7 +1159,7 @@ const TokenSwap = () => {
) : (
- No liquidity pools available yet + {t('tokenSwap.noPoolsAvailable')}
)}
@@ -1168,16 +1169,16 @@ const TokenSwap = () => {

- Recent Swaps + {t('tokenSwap.recentSwaps')}

{!selectedAccount ? (
- Connect wallet to view history + {t('tokenSwap.connectForHistory')}
) : isLoadingHistory ? (
- Loading history... + {t('tokenSwap.loadingHistory')}
) : swapHistory.length > 0 ? (
@@ -1196,11 +1197,11 @@ const TokenSwap = () => {
- Sent: + {t('tokenSwap.sent')} -{tx.fromAmount} {getTokenDisplayName(tx.fromToken)}
- Received: + {t('tokenSwap.received')} +{tx.toAmount} {getTokenDisplayName(tx.toToken)}
@@ -1213,7 +1214,7 @@ const TokenSwap = () => {
) : (
- No swap history yet + {t('tokenSwap.noSwapHistory')}
)} @@ -1222,11 +1223,11 @@ const TokenSwap = () => { - Swap Settings + {t('tokenSwap.swapSettings')}
- +
{['0.1', '0.5', '1.0'].map(val => (
diff --git a/web/src/components/TokenomicsSection.tsx b/web/src/components/TokenomicsSection.tsx index a2515a12..e0cf22e9 100644 --- a/web/src/components/TokenomicsSection.tsx +++ b/web/src/components/TokenomicsSection.tsx @@ -1,7 +1,9 @@ import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { PieChart, ArrowRightLeft } from 'lucide-react'; const TokenomicsSection: React.FC = () => { + const { t } = useTranslation(); const [selectedToken, setSelectedToken] = useState<'PEZ' | 'HEZ'>('PEZ'); const [monthsPassed] = useState(0); @@ -16,16 +18,16 @@ const TokenomicsSection: React.FC = () => { }, [monthsPassed, halvingPeriod, selectedToken]); const pezDistribution = [ - { name: 'Treasury', percentage: 96.25, amount: 4812500000, color: 'from-purple-500 to-purple-600' }, - { name: 'Presale', percentage: 1.875, amount: 93750000, color: 'from-cyan-500 to-cyan-600' }, - { name: 'Founder', percentage: 1.875, amount: 93750000, color: 'from-teal-500 to-teal-600' } + { name: t('tokenomics.treasury'), percentage: 96.25, amount: 4812500000, color: 'from-purple-500 to-purple-600' }, + { name: t('tokenomics.presale'), percentage: 1.875, amount: 93750000, color: 'from-cyan-500 to-cyan-600' }, + { name: t('tokenomics.founder'), percentage: 1.875, amount: 93750000, color: 'from-teal-500 to-teal-600' } ]; const hezDistribution = [ - { name: 'Staking Rewards', percentage: 40, amount: 1000000000, color: 'from-yellow-500 to-orange-600' }, - { name: 'Governance', percentage: 30, amount: 750000000, color: 'from-green-500 to-emerald-600' }, - { name: 'Ecosystem', percentage: 20, amount: 500000000, color: 'from-blue-500 to-indigo-600' }, - { name: 'Team', percentage: 10, amount: 250000000, color: 'from-red-500 to-pink-600' } + { name: t('tokenomics.stakingRewards'), percentage: 40, amount: 1000000000, color: 'from-yellow-500 to-orange-600' }, + { name: t('tokenomics.governance'), percentage: 30, amount: 750000000, color: 'from-green-500 to-emerald-600' }, + { name: t('tokenomics.ecosystem'), percentage: 20, amount: 500000000, color: 'from-blue-500 to-indigo-600' }, + { name: t('tokenomics.team'), percentage: 10, amount: 250000000, color: 'from-red-500 to-pink-600' } ]; const distribution = selectedToken === 'PEZ' ? pezDistribution : hezDistribution; @@ -37,10 +39,10 @@ const TokenomicsSection: React.FC = () => {

- Dual Token Ecosystem + {t('tokenomics.dualToken')}

- PEZ & HEZ tokens working together for governance and utility + {t('tokenomics.dualTokenDesc')}

{/* Token Selector */} @@ -53,17 +55,17 @@ const TokenomicsSection: React.FC = () => { : 'text-gray-400 hover:text-white' }`} > - PEZ Token + {t('tokenomics.pezToken')}
@@ -73,7 +75,7 @@ const TokenomicsSection: React.FC = () => {
-

{selectedToken} Distribution

+

{t('tokenomics.pezDistribution')}

@@ -99,7 +101,7 @@ const TokenomicsSection: React.FC = () => {
- Total Supply + {t('tokenomics.totalSupply')} {totalSupply.toLocaleString()} {selectedToken}
@@ -109,63 +111,63 @@ const TokenomicsSection: React.FC = () => {
-

{selectedToken} Features

+

{t('tokenomics.hezFeatures')}

{selectedToken === 'PEZ' ? (
-

Governance Token

-

Vote on proposals and participate in DAO decisions

+

{t('tokenomics.govToken')}

+

{t('tokenomics.govTokenDesc')}

-

Staking Rewards

-

Earn HEZ tokens by staking PEZ

+

{t('tokenomics.stakingRewardsTitle')}

+

{t('tokenomics.stakingRewardsDesc')}

-

Treasury Access

-

Propose and vote on treasury fund allocation

+

{t('tokenomics.treasuryAccess')}

+

{t('tokenomics.treasuryAccessDesc')}

-

Deflationary

-

Synthetic halving every 48 months

+

{t('tokenomics.deflationary')}

+

{t('tokenomics.deflationaryDesc')}

) : (
-

Utility Token

-

Used for platform transactions and services

+

{t('tokenomics.utilityToken')}

+

{t('tokenomics.utilityTokenDesc')}

-

P2P Trading

-

Primary currency for peer-to-peer marketplace

+

{t('tokenomics.p2pTrading')}

+

{t('tokenomics.p2pTradingDesc')}

-

Fee Discounts

-

Reduced platform fees when using HEZ

+

{t('tokenomics.feeDiscounts')}

+

{t('tokenomics.feeDiscountsDesc')}

-

Reward Distribution

-

Earned through staking and participation

+

{t('tokenomics.rewardDistribution')}

+

{t('tokenomics.rewardDistributionDesc')}

)}
-

Token Synergy

+

{t('tokenomics.tokenSynergy')}

- Stake PEZ → Earn HEZ rewards + {t('tokenomics.synergy1')}
- Use HEZ → Boost governance power + {t('tokenomics.synergy2')}
- Hold both → Maximum platform benefits + {t('tokenomics.synergy3')}
diff --git a/web/src/components/TransactionHistory.tsx b/web/src/components/TransactionHistory.tsx index 8b5b8a6e..9186d348 100644 --- a/web/src/components/TransactionHistory.tsx +++ b/web/src/components/TransactionHistory.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { Dialog, @@ -32,6 +33,7 @@ interface Transaction { export const TransactionHistory: React.FC = ({ isOpen, onClose }) => { const { api, isApiReady, selectedAccount } = usePezkuwi(); const { toast } = useToast(); + const { t } = useTranslation(); const [transactions, setTransactions] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -234,8 +236,8 @@ export const TransactionHistory: React.FC = ({ isOpen, } catch { if (import.meta.env.DEV) console.error('Failed to fetch transactions:', error); toast({ - title: "Error", - description: "Failed to fetch transaction history", + title: t('transfer.error'), + description: t('txHistory.fetchError'), variant: "destructive", }); } finally { @@ -272,9 +274,9 @@ export const TransactionHistory: React.FC = ({ isOpen,
- Transaction History + {t('txHistory.title')} - Recent transactions involving your account + {t('txHistory.description')}
diff --git a/web/src/components/TransferModal.tsx b/web/src/components/TransferModal.tsx index bfac28b6..b37d9688 100644 --- a/web/src/components/TransferModal.tsx +++ b/web/src/components/TransferModal.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { Dialog, @@ -68,6 +69,7 @@ const TOKENS: Token[] = [ export const TransferModal: React.FC = ({ isOpen, onClose, selectedAsset }) => { const { api, assetHubApi, isApiReady, isAssetHubReady, selectedAccount } = usePezkuwi(); const { toast } = useToast(); + const { t } = useTranslation(); const [selectedToken, setSelectedToken] = useState('HEZ'); const [recipient, setRecipient] = useState(''); @@ -90,8 +92,8 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s const handleTransfer = async () => { if (!api || !isApiReady || !selectedAccount) { toast({ - title: "Error", - description: "Wallet not connected", + title: t('transfer.error'), + description: t('transfer.walletNotConnected'), variant: "destructive", }); return; @@ -107,8 +109,8 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s currentToken.assetId === 1000; // wUSDT if (isAssetHubTransfer && (!assetHubApi || !isAssetHubReady)) { toast({ - title: "Error", - description: "Asset Hub connection not ready. This token is on Asset Hub.", + title: t('transfer.error'), + description: t('transfer.assetHubNotReady'), variant: "destructive", }); return; @@ -116,8 +118,8 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s if (!recipient || !amount) { toast({ - title: "Error", - description: "Please fill in all fields", + title: t('transfer.error'), + description: t('transfer.fillAllFields'), variant: "destructive", }); return; @@ -178,15 +180,15 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s setTxStatus('error'); toast({ - title: "Transfer Failed", + title: t('transfer.failed'), description: errorMessage, variant: "destructive", }); } else { setTxStatus('success'); toast({ - title: "Transfer Successful!", - description: `Sent ${amount} ${currentToken.symbol} to ${recipient.slice(0, 8)}...${recipient.slice(-6)}`, + title: t('transfer.success'), + description: t('transfer.sentAmount', { amount, token: currentToken.symbol, recipient: `${recipient.slice(0, 8)}...${recipient.slice(-6)}` }), }); // Reset form after 2 seconds @@ -210,8 +212,8 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s setIsTransferring(false); toast({ - title: "Transfer Failed", - description: error instanceof Error ? error.message : "An error occurred during transfer", + title: t('transfer.failed'), + description: error instanceof Error ? error.message : t('transfer.errorOccurred'), variant: "destructive", }); } @@ -233,23 +235,23 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s - {selectedAsset ? `Send ${selectedAsset.symbol}` : 'Send Tokens'} + {selectedAsset ? t('transfer.sendToken', { token: selectedAsset.symbol }) : t('transfer.sendTokens')} {selectedAsset - ? `Transfer ${selectedAsset.name} to another account` - : 'Transfer tokens to another account'} + ? t('transfer.transferTo', { name: selectedAsset.name }) + : t('transfer.transferTokens')} {txStatus === 'success' ? (
-

Transfer Successful!

-

Your transaction has been finalized

+

{t('transfer.success')}

+

{t('transfer.finalized')}

{txHash && (
-
Transaction Hash
+
{t('transfer.txHash')}
{txHash}
@@ -259,13 +261,13 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s ) : txStatus === 'error' ? (
-

Transfer Failed

-

Please try again

+

{t('transfer.failed')}

+

{t('transfer.pleaseTryAgain')}

) : ( @@ -273,10 +275,10 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s {/* Token Selection - Only show if no asset is pre-selected */} {!selectedAsset && (
- + setRecipient(e.target.value)} - placeholder="Recipient address" + placeholder={t('transfer.recipientPlaceholder')} className="bg-gray-800 border-gray-700 text-white mt-2 placeholder:text-gray-500 placeholder:opacity-50" disabled={isTransferring} />
- + setAmount(e.target.value)} - placeholder="Amount" + placeholder={t('transfer.amountPlaceholder')} className="bg-gray-800 border-gray-700 text-white mt-2 placeholder:text-gray-500 placeholder:opacity-50" disabled={isTransferring} />
- Decimals: {currentToken.decimals} + {t('transfer.decimals', { decimals: currentToken.decimals })}
{txStatus === 'signing' && (

- Please sign the transaction in your Pezkuwi.js extension + {t('transfer.signTransaction')}

)} @@ -342,7 +344,7 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s

- Transaction pending... Waiting for finalization + {t('transfer.txPending')}

)} @@ -355,11 +357,11 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s {isTransferring ? ( <> - {txStatus === 'signing' ? 'Waiting for signature...' : 'Processing...'} + {txStatus === 'signing' ? t('transfer.waitingSignature') : t('transfer.processing')} ) : ( <> - Send {selectedToken} + {t('transfer.sendToken', { token: selectedToken })} )} diff --git a/web/src/components/TrustScoreCalculator.tsx b/web/src/components/TrustScoreCalculator.tsx index b6b5d17c..82eb509a 100644 --- a/web/src/components/TrustScoreCalculator.tsx +++ b/web/src/components/TrustScoreCalculator.tsx @@ -1,7 +1,9 @@ import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { Calculator, TrendingUp, Users, BookOpen, Award } from 'lucide-react'; const TrustScoreCalculator: React.FC = () => { + const { t } = useTranslation(); const [stakedAmount, setStakedAmount] = useState(100); const [stakingMonths, setStakingMonths] = useState(6); const [referralCount, setReferralCount] = useState(5); @@ -56,10 +58,10 @@ const TrustScoreCalculator: React.FC = () => {

- Trust Score Calculator + {t('trustCalc.title')}

- Simulate your trust score based on staking, referrals, education, and roles + {t('trustCalc.description')}

@@ -70,12 +72,12 @@ const TrustScoreCalculator: React.FC = () => {
-

Staking Amount

+

{t('trustCalc.stakingTitle')}

- + { />
{stakedAmount} HEZ - Score: {getAmountScore(stakedAmount)} + {t('trustCalc.score', { score: getAmountScore(stakedAmount) })}
- + { className="w-full mt-2" />
- {stakingMonths} months - ×{getStakingMultiplier(stakingMonths).toFixed(1)} multiplier + {t('trustCalc.months', { count: stakingMonths })} + {t('trustCalc.multiplier', { value: getStakingMultiplier(stakingMonths).toFixed(1) })}
@@ -113,11 +115,11 @@ const TrustScoreCalculator: React.FC = () => {
-

Referral Score

+

{t('trustCalc.referralTitle')}

- + { className="w-full mt-2 px-4 py-2 bg-gray-800 text-white rounded-lg border border-gray-700 focus:border-cyan-500 focus:outline-none" />
- Score: {getReferralScore(referralCount)} points + {t('trustCalc.scorePoints', { score: getReferralScore(referralCount) })}
@@ -137,7 +139,7 @@ const TrustScoreCalculator: React.FC = () => {
-

Perwerde Score

+

{t('trustCalc.perwerdeTitle')}

{
-

Tiki Score

+

{t('trustCalc.tikiTitle')}

{ {/* Final Score */}
-

Final Trust Score

+

{t('trustCalc.resultTitle')}

{finalScore}
- Out of theoretical maximum + {t('trustCalc.resultHint')}
{/* Formula Breakdown */}
-

Formula Breakdown

+

{t('trustCalc.formulaTitle')}

@@ -209,19 +211,19 @@ const TrustScoreCalculator: React.FC = () => {
- Staking Component: + {t('trustCalc.formulaStaking')}: {Math.min(Math.round(getAmountScore(stakedAmount) * getStakingMultiplier(stakingMonths)), 100)} × 100
- Referral Component: + {t('trustCalc.formulaReferral')}: {getReferralScore(referralCount)} × 300
- Perwerde Component: + {t('trustCalc.formulaPerwerde')}: {perwerdeScore} × 300
- Tiki Component: + {t('trustCalc.formulaTiki')}: {tikiScore} × 300
@@ -229,16 +231,16 @@ const TrustScoreCalculator: React.FC = () => { {/* Score Impact */}
-

Score Impact

+

{t('trustCalc.impactTitle')}

- Monthly Rewards Eligibility + {t('trustCalc.rewards')} 100 ? 'bg-green-900/30 text-green-400' : 'bg-red-900/30 text-red-400'}`}> - {finalScore > 100 ? 'Eligible' : 'Not Eligible'} + {finalScore > 100 ? t('trustCalc.eligible') : t('trustCalc.notEligible')}
- Governance Voting Weight + {t('trustCalc.votingWeight')} {Math.min(Math.floor(finalScore / 100), 10)}x
diff --git a/web/src/components/USDTBridge.tsx b/web/src/components/USDTBridge.tsx index f4938e08..8ed9604e 100644 --- a/web/src/components/USDTBridge.tsx +++ b/web/src/components/USDTBridge.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { X, ArrowDown, ArrowUp, AlertCircle, Info, Clock, CheckCircle2 } from 'lucide-react'; import { web3FromAddress } from '@pezkuwi/extension-dapp'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; @@ -28,6 +29,7 @@ export const USDTBridge: React.FC = ({ onClose, specificAddresses = {}, }) => { + const { t } = useTranslation(); const { api, selectedAccount, isApiReady } = usePezkuwi(); const { refreshBalances } = useWallet(); @@ -59,7 +61,7 @@ export const USDTBridge: React.FC = ({ // Handle deposit (user requests deposit) const handleDeposit = async () => { if (!depositAmount || parseFloat(depositAmount) <= 0) { - setError('Please enter a valid amount'); + setError(t('bridge.invalidAmount')); return; } @@ -75,7 +77,7 @@ export const USDTBridge: React.FC = ({ // For now, just show instructions setSuccess( - `Deposit request for ${depositAmount} USDT created. Please follow the instructions to complete the deposit.` + t('bridge.depositSuccess', { amount: depositAmount }) ); setDepositAmount(''); } catch (err) { @@ -93,17 +95,17 @@ export const USDTBridge: React.FC = ({ const amount = parseFloat(withdrawAmount); if (!amount || amount <= 0) { - setError('Please enter a valid amount'); + setError(t('bridge.invalidAmount')); return; } if (amount > wusdtBalance) { - setError('Insufficient wUSDT balance'); + setError(t('bridge.insufficientBalance')); return; } if (!withdrawAddress) { - setError('Please enter withdrawal address'); + setError(t('bridge.noAddress')); return; } @@ -122,7 +124,7 @@ export const USDTBridge: React.FC = ({ if (status.isFinalized) { const delay = calculateWithdrawalDelay(amount); setSuccess( - `Withdrawal request submitted! wUSDT burned. USDT will be sent to ${withdrawAddress} after ${formatDelay(delay)}.` + t('bridge.withdrawSuccess', { address: withdrawAddress, delay: formatDelay(delay) }) ); setWithdrawAmount(''); setWithdrawAddress(''); @@ -148,8 +150,8 @@ export const USDTBridge: React.FC = ({ {/* Header */}
-

USDT Bridge

-

Deposit or withdraw USDT

+

{t('bridge.title')}

+

{t('bridge.subtitle')}

@@ -262,25 +264,25 @@ export const USDTBridge: React.FC = ({ -

How to Withdraw:

+

{t('bridge.withdrawHow')}

    -
  1. Burn your wUSDT on-chain
  2. -
  3. Wait for security delay ({withdrawalDelay > 0 && formatDelay(withdrawalDelay)})
  4. -
  5. Multisig (3/5) approves and sends USDT
  6. -
  7. Receive USDT to your specified address
  8. +
  9. {t('bridge.withdrawStep1')}
  10. +
  11. {t('bridge.withdrawStep2', { delay: withdrawalDelay > 0 ? formatDelay(withdrawalDelay) : '' })}
  12. +
  13. {t('bridge.withdrawStep3')}
  14. +
  15. {t('bridge.withdrawStep4')}
setWithdrawAmount(e.target.value)} - placeholder="Amount" + placeholder={t('bridge.amountPlaceholder')} max={wusdtBalance} className="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-3 text-white focus:outline-none focus:border-blue-500 placeholder:text-gray-500 placeholder:opacity-50" disabled={isLoading} @@ -289,19 +291,19 @@ export const USDTBridge: React.FC = ({ onClick={() => setWithdrawAmount(wusdtBalance.toString())} className="text-xs text-blue-400 hover:text-blue-300 mt-1" > - Max: {formatWUSDT(wusdtBalance)} + {t('bridge.max')}: {formatWUSDT(wusdtBalance)}
setWithdrawAddress(e.target.value)} - placeholder="Bank account or crypto address" + placeholder={t('bridge.addressPlaceholder')} className="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-3 text-white focus:outline-none focus:border-blue-500 placeholder:text-gray-500 placeholder:opacity-50" disabled={isLoading} /> @@ -310,17 +312,17 @@ export const USDTBridge: React.FC = ({ {withdrawAmount && parseFloat(withdrawAmount) > 0 && (
- You will receive: + {t('bridge.willReceive')} {withdrawAmount} USDT
- Withdrawal tier: + {t('bridge.withdrawTier')} {withdrawalTier}
- Security delay: + {t('bridge.securityDelay')} {formatDelay(withdrawalDelay)} @@ -337,12 +339,12 @@ export const USDTBridge: React.FC = ({ {isLoading ? (
- Processing... + {t('bridge.processing')}
) : (
- Withdraw USDT + {t('bridge.withdrawBtn')}
)} diff --git a/web/src/components/XCMTeleportModal.tsx b/web/src/components/XCMTeleportModal.tsx index 3c0df498..b9dc9192 100644 --- a/web/src/components/XCMTeleportModal.tsx +++ b/web/src/components/XCMTeleportModal.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { Dialog, @@ -55,6 +56,7 @@ interface XCMTeleportModalProps { export const XCMTeleportModal: React.FC = ({ isOpen, onClose }) => { const { api, assetHubApi, peopleApi, isApiReady, isAssetHubReady, isPeopleReady, selectedAccount } = usePezkuwi(); const { toast } = useToast(); + const { t } = useTranslation(); const [targetChain, setTargetChain] = useState('asset-hub'); const [amount, setAmount] = useState(''); @@ -66,6 +68,8 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl const [peopleBalance, setPeopleBalance] = useState('0'); const selectedChain = TARGET_CHAINS.find(c => c.id === targetChain)!; + const chainName = targetChain === 'asset-hub' ? t('xcm.assetHubName') : t('xcm.peopleName'); + const chainDesc = targetChain === 'asset-hub' ? t('xcm.assetHubDesc') : t('xcm.peopleDesc'); // Fetch balances useEffect(() => { @@ -121,8 +125,8 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl const handleTeleport = async () => { if (!api || !isApiReady || !selectedAccount) { toast({ - title: "Error", - description: "Wallet not connected", + title: t('common.error'), + description: t('xcm.walletNotConnected'), variant: "destructive", }); return; @@ -130,8 +134,8 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl if (!amount || parseFloat(amount) <= 0) { toast({ - title: "Error", - description: "Please enter a valid amount", + title: t('common.error'), + description: t('xcm.invalidAmount'), variant: "destructive", }); return; @@ -142,8 +146,8 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl if (sendAmount > currentBalance) { toast({ - title: "Error", - description: "Insufficient balance on Relay Chain", + title: t('common.error'), + description: t('xcm.insufficientBalance'), variant: "destructive", }); return; @@ -236,7 +240,7 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl if (status.isFinalized) { if (dispatchError) { - let errorMessage = 'Teleport failed'; + let errorMessage = t('xcm.failed'); if (dispatchError.isModule) { const decoded = api.registry.findMetaError(dispatchError.asModule); @@ -245,15 +249,15 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl setTxStatus('error'); toast({ - title: "Teleport Failed", + title: t('xcm.failed'), description: errorMessage, variant: "destructive", }); } else { setTxStatus('success'); toast({ - title: "Teleport Successful!", - description: `${amount} HEZ teleported to ${selectedChain.name}!`, + title: t('xcm.success'), + description: t('xcm.sentTo', { amount, chain: chainName }), }); // Reset after success @@ -276,8 +280,8 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl setIsTransferring(false); toast({ - title: "Teleport Failed", - description: error instanceof Error ? error.message : "An error occurred", + title: t('xcm.failed'), + description: error instanceof Error ? error.message : t('xcm.errorOccurred'), variant: "destructive", }); } @@ -306,21 +310,21 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl HEZ - Teleport HEZ to Teyrchain + {t('xcm.title')} - Transfer HEZ from Pezkuwi (Relay Chain) to a teyrchain for transaction fees + {t('xcm.description')} {txStatus === 'success' ? (
-

Teleport Successful!

-

{amount} HEZ sent to {selectedChain.name}

+

{t('xcm.success')}

+

{t('xcm.sentTo', { amount, chain: chainName })}

{txHash && (
-
Transaction Hash
+
{t('xcm.txHash')}
{txHash}
)} @@ -328,20 +332,20 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl ) : txStatus === 'error' ? (
-

Teleport Failed

-

Please try again

+

{t('xcm.failed')}

+

{t('xcm.pleaseTryAgain')}

) : (
{/* Target Chain Selection */}
- + = ({ isOpen, onCl {txStatus === 'signing' && (

- Please sign the transaction in your wallet extension + {t('xcm.signTransaction')}

)} @@ -437,7 +441,7 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl

- XCM Teleport in progress... This may take a moment. + {t('xcm.inProgress')}

)} @@ -451,11 +455,11 @@ export const XCMTeleportModal: React.FC = ({ isOpen, onCl {isTransferring ? ( <> - {txStatus === 'signing' ? 'Waiting for signature...' : 'Processing XCM...'} + {txStatus === 'signing' ? t('xcm.waitingSignature') : t('xcm.processingXcm')} ) : ( <> - Teleport HEZ to {selectedChain.name} + {t('xcm.teleportTo', { chain: chainName })} )} diff --git a/web/src/components/admin/CommissionSetupTab.tsx b/web/src/components/admin/CommissionSetupTab.tsx index d146d06e..66f91fd7 100644 --- a/web/src/components/admin/CommissionSetupTab.tsx +++ b/web/src/components/admin/CommissionSetupTab.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; @@ -9,6 +10,7 @@ import { COMMISSIONS } from '@/config/commissions'; import { Alert, AlertDescription } from '@/components/ui/alert'; export function CommissionSetupTab() { + const { t } = useTranslation(); const { api, isApiReady, selectedAccount } = usePezkuwi(); const { toast } = useToast(); @@ -50,8 +52,8 @@ export function CommissionSetupTab() { const handleAddMember = async () => { if (!api || !selectedAccount) { toast({ - title: 'Wallet Not Connected', - description: 'Please connect your admin wallet', + title: t('commission.setup.walletNotConnected'), + description: t('commission.setup.connectWallet'), variant: 'destructive', }); return; @@ -59,8 +61,8 @@ export function CommissionSetupTab() { if (!newMemberAddress) { toast({ - title: 'No Addresses', - description: 'Please enter at least one address', + title: t('commission.setup.noAddresses'), + description: t('commission.setup.enterAtLeastOne'), variant: 'destructive', }); return; @@ -79,8 +81,8 @@ export function CommissionSetupTab() { if (newAddresses.length === 0) { toast({ - title: 'No Valid Addresses', - description: 'Please enter at least one valid address', + title: t('commission.setup.noValidAddresses'), + description: t('commission.setup.enterValidAddress'), variant: 'destructive', }); setProcessing(false); @@ -96,8 +98,8 @@ export function CommissionSetupTab() { if (newMembers.length === 0) { toast({ - title: 'Already Members', - description: 'All addresses are already commission members', + title: t('commission.setup.alreadyInitialized'), + description: t('commission.setup.alreadyInitialized'), variant: 'destructive', }); setProcessing(false); @@ -125,21 +127,20 @@ export function CommissionSetupTab() { ({ status, dispatchError }) => { if (status.isInBlock || status.isFinalized) { if (dispatchError) { - let errorMessage = 'Failed to add member'; + let errorMessage = t('commission.setup.addMemberFailed'); if (dispatchError.isModule) { const decoded = api.registry.findMetaError(dispatchError.asModule); errorMessage = `${decoded.section}.${decoded.name}`; } toast({ - title: 'Error', + title: t('commission.setup.addMemberFailed'), description: errorMessage, variant: 'destructive', }); reject(new Error(errorMessage)); } else { toast({ - title: 'Success', - description: `${newMembers.length} member(s) added successfully!`, + title: t('commission.setup.addMemberSuccess', { count: newMembers.length }), }); setNewMemberAddress(''); setTimeout(() => checkSetup(), 2000); @@ -152,8 +153,8 @@ export function CommissionSetupTab() { } catch (error) { if (import.meta.env.DEV) console.error('Error adding member:', error); toast({ - title: 'Error', - description: error instanceof Error ? error.message : 'Failed to add member', + title: t('commission.setup.addMemberFailed'), + description: error instanceof Error ? error.message : t('commission.setup.addMemberFailed'), variant: 'destructive', }); } finally { @@ -164,8 +165,8 @@ export function CommissionSetupTab() { const handleInitializeCommission = async () => { if (!api || !selectedAccount) { toast({ - title: 'Wallet Not Connected', - description: 'Please connect your admin wallet', + title: t('commission.setup.walletNotConnected'), + description: t('commission.setup.connectWallet'), variant: 'destructive', }); return; @@ -209,7 +210,7 @@ export function CommissionSetupTab() { if (import.meta.env.DEV) console.error('Setup error:', errorMessage); toast({ - title: 'Setup Failed', + title: t('commission.setup.setupFailed'), description: errorMessage, variant: 'destructive', }); @@ -225,8 +226,7 @@ export function CommissionSetupTab() { if (sudidEvent) { if (import.meta.env.DEV) console.log('✅ KYC Commission initialized'); toast({ - title: 'Success', - description: 'KYC Commission initialized successfully!', + title: t('commission.setup.kycInitialized'), }); resolve(); } else { @@ -238,8 +238,8 @@ export function CommissionSetupTab() { ).catch((error) => { if (import.meta.env.DEV) console.error('Failed to sign and send:', error); toast({ - title: 'Transaction Error', - description: error instanceof Error ? error.message : 'Failed to submit transaction', + title: t('commission.setup.transactionError'), + description: error instanceof Error ? error.message : t('commission.setup.failedToSubmit'), variant: 'destructive', }); reject(error); @@ -252,8 +252,8 @@ export function CommissionSetupTab() { } catch (error) { if (import.meta.env.DEV) console.error('Error initializing commission:', error); toast({ - title: 'Error', - description: error instanceof Error ? error.message : 'Failed to initialize commission', + title: t('commission.setup.setupFailed'), + description: error instanceof Error ? error.message : t('commission.setup.failedToInitialize'), variant: 'destructive', }); } finally { @@ -267,7 +267,7 @@ export function CommissionSetupTab() {
- Connecting to blockchain... + {t('commission.setup.connecting')}
@@ -281,7 +281,7 @@ export function CommissionSetupTab() { - Please connect your admin wallet to manage commission setup. + {t('commission.setup.connectWalletAlert')} @@ -296,7 +296,7 @@ export function CommissionSetupTab() { - KYC Commission Setup + {t('commission.setup.statusLabel')} @@ -308,28 +308,28 @@ export function CommissionSetupTab() { <>
-

Commission Status

+

{t('commission.setup.statusLabel')}

{setupComplete - ? 'Commission is initialized and ready' - : 'Commission needs to be initialized'} + ? t('commission.setup.initialized') + : t('commission.setup.notInitialized')}

{setupComplete ? ( - Ready + {t('commission.setup.ready')} ) : ( - Not Initialized + {t('commission.setup.notInitializedBadge')} )}
-

Proxy Account

+

{t('commission.setup.proxyAccount')}

{COMMISSIONS.KYC.proxyAccount}

@@ -337,11 +337,11 @@ export function CommissionSetupTab() {

- Commission Members ({commissionMembers.length}) + {t('commission.setup.membersLabel')} ({commissionMembers.length})

{commissionMembers.length === 0 ? (
- No members yet + {t('commission.setup.noMembers')}
) : (
@@ -352,7 +352,7 @@ export function CommissionSetupTab() { >

{member}

{member === COMMISSIONS.KYC.proxyAccount && ( - KYC Proxy + {t('commission.setup.kycProxy')} )}
))} @@ -364,34 +364,33 @@ export function CommissionSetupTab() { - Required: Initialize the commission before members can join. - This requires sudo privileges. + {t('commission.setup.initRequired')} )} {setupComplete && (
-

Add Members

+

{t('commission.setup.addMembersTitle')}

- +
-

Our Focus Areas

+

{t('grants.focusAreas')}

    -
  • Decentralized Finance (DeFi)
  • -
  • NFTs and Gaming
  • -
  • Infrastructure and Tooling
  • -
  • Governance and DAOs
  • -
  • Privacy and Identity
  • -
  • Mobile and Web3 Applications
  • +
  • {t('grants.defi')}
  • +
  • {t('grants.nfts')}
  • +
  • {t('grants.infrastructure')}
  • +
  • {t('grants.governanceDao')}
  • +
  • {t('grants.privacy')}
  • +
  • {t('grants.mobile')}
-

Funded Projects

+

{t('grants.fundedProjects')}

{fundedProjects.map((project, index) => (
diff --git a/web/src/pages/NotFound.tsx b/web/src/pages/NotFound.tsx index 04b0a6f3..1ac301d3 100644 --- a/web/src/pages/NotFound.tsx +++ b/web/src/pages/NotFound.tsx @@ -1,7 +1,9 @@ import { useLocation } from "react-router-dom"; import { useEffect } from "react"; +import { useTranslation } from 'react-i18next'; const NotFound = () => { + const { t } = useTranslation(); const location = useLocation(); useEffect(() => { @@ -14,10 +16,10 @@ const NotFound = () => { return (
-

404

-

Page not found

+

{t('notFound.code')}

+

{t('notFound.message')}

- Return to Home + {t('notFound.backToHome')}
diff --git a/web/src/pages/P2PDispute.tsx b/web/src/pages/P2PDispute.tsx index 2652cb03..dc454a94 100644 --- a/web/src/pages/P2PDispute.tsx +++ b/web/src/pages/P2PDispute.tsx @@ -71,39 +71,39 @@ interface Evidence { created_at: string; } -const STATUS_CONFIG: Record = { +const STATUS_CONFIG: Record = { open: { color: 'bg-amber-500', icon: , - label: 'Open', + labelKey: 'p2pDispute.statusOpen', }, under_review: { color: 'bg-blue-500', icon: , - label: 'Under Review', + labelKey: 'p2pDispute.statusUnderReview', }, resolved: { color: 'bg-green-500', icon: , - label: 'Resolved', + labelKey: 'p2pDispute.statusResolved', }, closed: { color: 'bg-gray-500', icon: , - label: 'Closed', + labelKey: 'p2pDispute.statusClosed', }, }; -const RESOLUTION_LABELS: Record = { - release_to_buyer: 'Released to Buyer', - refund_to_seller: 'Refunded to Seller', - split: 'Split Decision', +const RESOLUTION_LABEL_KEYS: Record = { + release_to_buyer: 'p2pDispute.releasedToBuyer', + refund_to_seller: 'p2pDispute.refundedToSeller', + split: 'p2pDispute.splitDecision', }; export default function P2PDispute() { const { disputeId } = useParams<{ disputeId: string }>(); const navigate = useNavigate(); - useTranslation(); + const { t } = useTranslation(); const fileInputRef = useRef(null); const [dispute, setDispute] = useState(null); @@ -150,7 +150,7 @@ export default function P2PDispute() { } } catch (error) { console.error('Failed to fetch dispute:', error); - toast.error('Failed to load dispute details'); + toast.error(t('p2pDispute.failedToLoad')); } finally { setIsLoading(false); } @@ -205,7 +205,7 @@ export default function P2PDispute() { try { for (const file of Array.from(files)) { if (file.size > 10 * 1024 * 1024) { - toast.error(`File ${file.name} is too large (max 10MB)`); + toast.error(t('p2pDispute.fileTooLarge', { name: file.name })); continue; } @@ -230,10 +230,10 @@ export default function P2PDispute() { }); } - toast.success('Evidence uploaded successfully'); + toast.success(t('p2pDispute.evidenceUploaded')); } catch (error) { console.error('Upload failed:', error); - toast.error('Failed to upload evidence'); + toast.error(t('p2pDispute.failedToUpload')); } finally { setIsUploading(false); if (fileInputRef.current) { @@ -273,12 +273,12 @@ export default function P2PDispute() { -

Dispute Not Found

+

{t('p2pDispute.notFound')}

- The dispute you are looking for does not exist or you do not have access. + {t('p2pDispute.notFoundDesc')}

@@ -297,7 +297,7 @@ export default function P2PDispute() { className="gap-2" > - Back + {t('p2p.back')} {/* Header Card */} @@ -307,16 +307,16 @@ export default function P2PDispute() {
- Dispute #{dispute.id.slice(0, 8)} + {t('p2pDispute.disputeId', { id: dispute.id.slice(0, 8) })} - Opened {new Date(dispute.created_at).toLocaleDateString()} + {t('p2pDispute.opened', { date: new Date(dispute.created_at).toLocaleDateString() })}
{statusConfig.icon} - {statusConfig.label} + {t(statusConfig.labelKey)}
@@ -326,32 +326,31 @@ export default function P2PDispute() {
-

Related Trade

+

{t('p2pDispute.relatedTrade')}

- {dispute.trade.crypto_amount} {dispute.trade.token} for{' '} - {dispute.trade.fiat_amount} {dispute.trade.fiat_currency} + {t('p2pDispute.tradeAmountFor', { cryptoAmount: dispute.trade.crypto_amount, token: dispute.trade.token, fiatAmount: dispute.trade.fiat_amount, currency: dispute.trade.fiat_currency })}

- Buyer:{' '} + {t('p2p.buyer')}:{' '} {formatAddress(dispute.trade.buyer_wallet, 6, 4)} - {isBuyer && ' (You)'} + {isBuyer && ` ${t('p2p.you')}`}
- Seller:{' '} + {t('p2p.seller')}:{' '} {formatAddress(dispute.trade.seller_wallet, 6, 4)} - {isSeller && ' (You)'} + {isSeller && ` ${t('p2p.you')}`}
@@ -362,7 +361,7 @@ export default function P2PDispute() {

- Dispute Reason + {t('p2pDispute.disputeReason')}

{dispute.reason.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())} @@ -371,7 +370,7 @@ export default function P2PDispute() { {dispute.description}

- Opened by: {isOpener ? 'You' : (isBuyer ? 'Seller' : 'Buyer')} + {t('p2pDispute.openedBy', { party: isOpener ? t('p2p.you').replace(/[()]/g, '') : (isBuyer ? t('p2p.seller') : t('p2p.buyer')) })}

@@ -380,10 +379,10 @@ export default function P2PDispute() {

- Resolution + {t('p2pDispute.resolutionTitle')}

- {RESOLUTION_LABELS[dispute.resolution]} + {t(RESOLUTION_LABEL_KEYS[dispute.resolution])}

{dispute.resolution_notes && (

@@ -392,7 +391,7 @@ export default function P2PDispute() { )} {dispute.resolved_at && (

- Resolved on {new Date(dispute.resolved_at).toLocaleString()} + {t('p2pDispute.resolvedOn', { date: new Date(dispute.resolved_at).toLocaleString() })}

)}
@@ -404,7 +403,7 @@ export default function P2PDispute() {
- Evidence + {t('p2pDispute.evidenceTitle')} {isParticipant && dispute.status !== 'resolved' && (
- {isUploading ? 'Uploading...' : 'Add Evidence'} + {isUploading ? t('p2pDispute.uploading') : t('p2pDispute.addEvidence')}
)} @@ -432,10 +431,10 @@ export default function P2PDispute() { {evidence.length === 0 ? (
-

No evidence submitted yet

+

{t('p2pDispute.noEvidence')}

{isParticipant && dispute.status !== 'resolved' && (

- Upload screenshots, receipts, or documents to support your case + {t('p2pDispute.uploadEvidenceHelp')}

)}
@@ -468,8 +467,8 @@ export default function P2PDispute() {

{item.description}

- {isMyEvidence ? 'You' : ( - item.uploaded_by === dispute.trade?.buyer_id ? 'Buyer' : 'Seller' + {isMyEvidence ? t('p2p.you').replace(/[()]/g, '') : ( + item.uploaded_by === dispute.trade?.buyer_id ? t('p2p.buyer') : t('p2p.seller') )}

@@ -491,7 +490,7 @@ export default function P2PDispute() { {/* Status Timeline */} - Status Timeline + {t('p2pDispute.statusTimeline')}
@@ -501,7 +500,7 @@ export default function P2PDispute() {
-

Dispute Opened

+

{t('p2pDispute.disputeOpenedStep')}

{new Date(dispute.created_at).toLocaleString()}

@@ -515,9 +514,9 @@ export default function P2PDispute() {
-

Under Review

+

{t('p2pDispute.underReviewStep')}

- Admin is reviewing the case + {t('p2pDispute.adminReviewing')}

@@ -530,7 +529,7 @@ export default function P2PDispute() {
-

Resolved

+

{t('p2pDispute.resolvedStep')}

{dispute.resolved_at && new Date(dispute.resolved_at).toLocaleString()}

@@ -546,8 +545,8 @@ export default function P2PDispute() {
-

Under Review

-

Pending

+

{t('p2pDispute.underReviewStep')}

+

{t('p2pDispute.pending')}

@@ -555,8 +554,8 @@ export default function P2PDispute() {
-

Resolution

-

Pending

+

{t('p2pDispute.resolutionStep')}

+

{t('p2pDispute.pending')}

@@ -571,13 +570,13 @@ export default function P2PDispute() {
-

Need Help?

+

{t('p2pDispute.needHelp')}

- Our support team typically responds within 24-48 hours. + {t('p2pDispute.supportResponse')}

diff --git a/web/src/pages/P2PMerchantDashboard.tsx b/web/src/pages/P2PMerchantDashboard.tsx index da3b9b76..f0a5d172 100644 --- a/web/src/pages/P2PMerchantDashboard.tsx +++ b/web/src/pages/P2PMerchantDashboard.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; import { supabase } from '@/lib/supabase'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -89,6 +90,7 @@ interface ChartDataPoint { export default function P2PMerchantDashboard() { const navigate = useNavigate(); + const { t } = useTranslation(); const [loading, setLoading] = useState(true); const [stats, setStats] = useState(null); const [tierInfo, setTierInfo] = useState(null); @@ -212,17 +214,17 @@ export default function P2PMerchantDashboard() { if (error) throw error; - toast.success(`Ad ${newStatus === 'open' ? 'activated' : 'paused'}`); + toast.success(newStatus === 'open' ? t('p2pMerchant.adActivated') : t('p2pMerchant.adPaused')); fetchData(); } catch (error) { console.error('Error toggling ad status:', error); - toast.error('Failed to update ad status'); + toast.error(t('p2pMerchant.failedToUpdateStatus')); } }; // Delete ad const deleteAd = async (adId: string) => { - if (!confirm('Are you sure you want to delete this ad?')) return; + if (!confirm(t('p2pMerchant.confirmDelete'))) return; try { const { error } = await supabase @@ -232,11 +234,11 @@ export default function P2PMerchantDashboard() { if (error) throw error; - toast.success('Ad deleted successfully'); + toast.success(t('p2pMerchant.adDeleted')); fetchData(); } catch (error) { console.error('Error deleting ad:', error); - toast.error('Failed to delete ad'); + toast.error(t('p2pMerchant.failedToDelete')); } }; @@ -255,7 +257,7 @@ export default function P2PMerchantDashboard() { const fiatAmt = parseFloat(editFiatAmount); if (!fiatAmt || fiatAmt <= 0) { - toast.error('Invalid fiat amount'); + toast.error(t('p2pMerchant.invalidFiatAmount')); return; } @@ -274,13 +276,13 @@ export default function P2PMerchantDashboard() { if (error) throw error; - toast.success('Ad updated successfully'); + toast.success(t('p2pMerchant.adUpdated')); setEditAdOpen(false); setEditingAd(null); fetchData(); } catch (error) { console.error('Error updating ad:', error); - toast.error('Failed to update ad'); + toast.error(t('p2pMerchant.failedToUpdate')); } finally { setSavingEdit(false); } @@ -298,11 +300,11 @@ export default function P2PMerchantDashboard() { if (error) throw error; - toast.success('Auto-reply message saved'); + toast.success(t('p2pMerchant.autoReplySaved')); setAutoReplyOpen(false); } catch (error) { console.error('Error saving auto-reply:', error); - toast.error('Failed to save auto-reply'); + toast.error(t('p2pMerchant.failedToSaveAutoReply')); } finally { setSavingAutoReply(false); } @@ -327,10 +329,10 @@ export default function P2PMerchantDashboard() {

- Merchant Dashboard + {t('p2pMerchant.dashboardTitle')}

- Manage your P2P trading business + {t('p2pMerchant.dashboardSubtitle')}

@@ -341,19 +343,19 @@ export default function P2PMerchantDashboard() { - Overview + {t('p2pMerchant.tabOverview')} - My Ads + {t('p2pMerchant.tabAds')} - Upgrade Tier + {t('p2pMerchant.tabUpgrade')} - Settings + {t('p2pMerchant.tabSettings')} @@ -365,7 +367,7 @@ export default function P2PMerchantDashboard() {
-

30-Day Volume

+

{t('p2pMerchant.thirtyDayVolumeLabel')}

${stats?.total_volume_30d?.toLocaleString() || '0'}

@@ -379,7 +381,7 @@ export default function P2PMerchantDashboard() {
-

30-Day Trades

+

{t('p2pMerchant.thirtyDayTrades')}

{stats?.total_trades_30d || 0}

@@ -391,7 +393,7 @@ export default function P2PMerchantDashboard() {
-

Completion Rate

+

{t('p2pMerchant.completionRateLabel')}

{stats?.completion_rate_30d?.toFixed(1) || '0'}%

@@ -405,7 +407,7 @@ export default function P2PMerchantDashboard() {
-

Avg Release Time

+

{t('p2pMerchant.avgReleaseTime')}

{stats?.avg_release_time_minutes || 0}m

@@ -421,8 +423,8 @@ export default function P2PMerchantDashboard() { {/* Volume Chart */} - Volume Trend - Last 30 days trading volume + {t('p2pMerchant.volumeTrend')} + {t('p2pMerchant.last30dVolume')} @@ -451,8 +453,8 @@ export default function P2PMerchantDashboard() { {/* Trades Chart */} - Trade Count - Daily trades over last 30 days + {t('p2pMerchant.tradeCount')} + {t('p2pMerchant.dailyTrades30d')} @@ -476,7 +478,7 @@ export default function P2PMerchantDashboard() { {/* Quick Stats */} - Lifetime Statistics + {t('p2pMerchant.lifetimeStats')}
@@ -484,23 +486,23 @@ export default function P2PMerchantDashboard() {

${stats?.total_volume_lifetime?.toLocaleString() || '0'}

-

Total Volume

+

{t('p2pMerchant.totalVolume')}

{stats?.total_trades_lifetime || 0}

-

Total Trades

+

{t('p2pMerchant.totalTrades')}

${stats?.buy_volume_30d?.toLocaleString() || '0'}

-

Buy Volume (30d)

+

{t('p2pMerchant.buyVolume30d')}

${stats?.sell_volume_30d?.toLocaleString() || '0'}

-

Sell Volume (30d)

+

{t('p2pMerchant.sellVolume30d')}

@@ -511,14 +513,14 @@ export default function P2PMerchantDashboard() {

- Active Advertisements ({activeAds.length}) + {t('p2pMerchant.activeAds', { count: activeAds.length })}

@@ -527,11 +529,11 @@ export default function P2PMerchantDashboard() {

- You don't have any active ads yet. + {t('p2pMerchant.noAdsYet')}

@@ -548,17 +550,17 @@ export default function P2PMerchantDashboard() { {ad.status.toUpperCase()} - Sell {ad.token} for {ad.fiat_currency} + {t('p2pMerchant.sellTokenFor', { token: ad.token, currency: ad.fiat_currency })} {ad.is_featured && ( - Featured + {t('p2pMerchant.featured')} )}

- {ad.remaining_amount} / {ad.amount_crypto} {ad.token} remaining + {t('p2pMerchant.remaining', { remaining: ad.remaining_amount, total: ad.amount_crypto, token: ad.token })}

@@ -568,7 +570,7 @@ export default function P2PMerchantDashboard() { {ad.price_per_unit?.toFixed(2)} {ad.fiat_currency}/{ad.token}

- Total: {ad.fiat_amount?.toLocaleString()} {ad.fiat_currency} + {t('p2pMerchant.total', { amount: ad.fiat_amount?.toLocaleString(), currency: ad.fiat_currency })}

@@ -577,7 +579,7 @@ export default function P2PMerchantDashboard() { variant="ghost" size="icon" onClick={() => toggleAdStatus(ad.id, ad.status)} - title={ad.status === 'open' ? 'Pause' : 'Activate'} + title={ad.status === 'open' ? t('p2pMerchant.pause') : t('p2pMerchant.activate')} > {ad.status === 'open' ? ( @@ -589,7 +591,7 @@ export default function P2PMerchantDashboard() { variant="ghost" size="icon" onClick={() => openEditModal(ad)} - title="Edit" + title={t('p2pMerchant.edit')} > @@ -597,7 +599,7 @@ export default function P2PMerchantDashboard() { variant="ghost" size="icon" onClick={() => deleteAd(ad.id)} - title="Delete" + title={t('p2pMerchant.delete')} className="text-destructive hover:text-destructive" > @@ -616,13 +618,13 @@ export default function P2PMerchantDashboard() {
- Active ads: {activeAds.filter(a => a.status === 'open').length} / {tierInfo.max_pending_orders} + {t('p2pMerchant.activeAdsCount', { current: activeAds.filter(a => a.status === 'open').length, max: tierInfo.max_pending_orders })} - Max order: ${tierInfo.max_order_amount.toLocaleString()} + {t('p2pMerchant.maxOrderLimit', { amount: tierInfo.max_order_amount.toLocaleString() })} - Featured ads: {activeAds.filter(a => a.is_featured).length} / {tierInfo.featured_ads_allowed} + {t('p2pMerchant.featuredAdsCount', { current: activeAds.filter(a => a.is_featured).length, max: tierInfo.featured_ads_allowed })}
@@ -642,16 +644,16 @@ export default function P2PMerchantDashboard() { - Auto-Reply Message + {t('p2pMerchant.autoReplyTitle')} - Automatically send this message when someone starts a trade with you + {t('p2pMerchant.autoReplyDesc')} @@ -659,32 +661,32 @@ export default function P2PMerchantDashboard() { {/* Notification Settings */} - Notification Settings + {t('p2pMerchant.notificationSettings')}
-

New Order Notifications

+

{t('p2pMerchant.newOrderNotifications')}

- Get notified when someone accepts your offer + {t('p2pMerchant.newOrderNotificationsDesc')}

-

Payment Notifications

+

{t('p2pMerchant.paymentNotifications')}

- Get notified when buyer marks payment as sent + {t('p2pMerchant.paymentNotificationsDesc')}

-

Chat Notifications

+

{t('p2pMerchant.chatNotifications')}

- Get notified for new chat messages + {t('p2pMerchant.chatNotificationsDesc')}

@@ -695,17 +697,17 @@ export default function P2PMerchantDashboard() { {/* Danger Zone */} - Danger Zone + {t('p2pMerchant.dangerZone')}
-

Pause All Ads

+

{t('p2pMerchant.pauseAllAds')}

- Temporarily pause all your active advertisements + {t('p2pMerchant.pauseAllAdsDesc')}

- +
@@ -716,25 +718,25 @@ export default function P2PMerchantDashboard() { - Auto-Reply Message + {t('p2pMerchant.autoReplyDialogTitle')} - This message will be automatically sent when someone starts a trade with you. + {t('p2pMerchant.autoReplyDialogDesc')}
- +