From 590ac521e8ed83043eac54cce5097bba4754465a Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Wed, 19 Nov 2025 18:56:38 +0300 Subject: [PATCH] refactor(core): Apply various updates and fixes across components --- shared/lib/guards.ts | 5 +- web/src/App.tsx | 7 +- web/src/components/NftList.tsx | 14 +- web/src/components/ProtectedRoute.tsx | 91 ++++++- web/src/components/TokenSwap.tsx | 8 +- web/src/components/TransferModal.tsx | 8 +- web/src/components/USDTBridge.tsx | 12 +- .../components/admin/CommissionSetupTab.tsx | 4 +- .../citizenship/CitizenshipModal.tsx | 11 +- .../citizenship/ExistingCitizenAuth.tsx | 49 ++-- .../citizenship/NewCitizenApplication.tsx | 205 +++++++++++--- web/src/components/p2p/CreateAd.tsx | 29 +- web/src/components/p2p/TradeModal.tsx | 4 +- web/src/contexts/AuthContext.tsx | 69 +++-- web/src/contexts/PolkadotContext.tsx | 73 ++++- web/src/contexts/WebSocketContext.tsx | 7 +- web/src/pages/AdminPanel.tsx | 73 +++-- web/src/pages/BeCitizen.tsx | 145 ++++++---- web/src/pages/Dashboard.tsx | 257 +++++++++++++++++- web/src/pages/Login.tsx | 4 +- web/src/pages/citizens/CitizensIssues.tsx | 53 +++- 21 files changed, 888 insertions(+), 240 deletions(-) diff --git a/shared/lib/guards.ts b/shared/lib/guards.ts index 18eb7352..305e655b 100644 --- a/shared/lib/guards.ts +++ b/shared/lib/guards.ts @@ -78,8 +78,9 @@ export async function checkValidatorStatus( // ======================================== // Tiki role enum mapping (from pallet-tiki) +// IMPORTANT: Must match /Pezkuwi-SDK/pezkuwi/pallets/tiki/src/lib.rs const TIKI_ROLES = [ - 'Hemwelatî', // 0 - Citizen + 'Welati', // 0 - Citizen 'Parlementer', // 1 - Parliament Member 'SerokiMeclise', // 2 - Speaker of Parliament 'Serok', // 3 - President @@ -127,7 +128,7 @@ const TIKI_ROLES = [ /** * Check if user has specific Tiki role - * @param role - Kurdish name of role (e.g., 'Hemwelatî', 'Perwerdekar') + * @param role - Kurdish name of role (e.g., 'Welati', 'Perwerdekar') */ export async function checkTikiRole( api: ApiPromise | null, diff --git a/web/src/App.tsx b/web/src/App.tsx index e1963249..746e3fe8 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -24,6 +24,7 @@ import { WebSocketProvider } from '@/contexts/WebSocketContext'; import { IdentityProvider } from '@/contexts/IdentityContext'; import { AuthProvider } from '@/contexts/AuthContext'; import { DashboardProvider } from '@/contexts/DashboardContext'; +import { ReferralProvider } from '@/contexts/ReferralContext'; import { ProtectedRoute } from '@/components/ProtectedRoute'; import NotFound from '@/pages/NotFound'; import { Toaster } from '@/components/ui/toaster'; @@ -42,7 +43,8 @@ function App() { - + + } /> @@ -100,7 +102,8 @@ function App() { } /> } /> - + + diff --git a/web/src/components/NftList.tsx b/web/src/components/NftList.tsx index c681f560..21c758ce 100644 --- a/web/src/components/NftList.tsx +++ b/web/src/components/NftList.tsx @@ -10,13 +10,13 @@ import type { TikiInfo } from '@pezkuwi/lib/citizenship-workflow'; const getTikiIcon = (role: string) => { const roleLower = role.toLowerCase(); - if (roleLower.includes('hemwelatî') || roleLower.includes('welati') || roleLower.includes('citizen')) { + if (roleLower.includes('welati') || roleLower.includes('citizen')) { return ; } - if (roleLower.includes('leader') || roleLower.includes('chief')) { + if (roleLower.includes('serok') || roleLower.includes('leader') || roleLower.includes('chief')) { return ; } - if (roleLower.includes('elder') || roleLower.includes('wise')) { + if (roleLower.includes('axa') || roleLower.includes('hekem') || roleLower.includes('elder') || roleLower.includes('wise')) { return ; } return ; @@ -26,13 +26,13 @@ const getTikiIcon = (role: string) => { const getRoleBadgeColor = (role: string) => { const roleLower = role.toLowerCase(); - if (roleLower.includes('hemwelatî') || roleLower.includes('welati') || roleLower.includes('citizen')) { + if (roleLower.includes('welati') || roleLower.includes('citizen')) { return 'bg-cyan-500/10 text-cyan-500 border-cyan-500/30'; } - if (roleLower.includes('leader') || roleLower.includes('chief')) { + if (roleLower.includes('serok') || roleLower.includes('leader') || roleLower.includes('chief')) { return 'bg-yellow-500/10 text-yellow-500 border-yellow-500/30'; } - if (roleLower.includes('elder') || roleLower.includes('wise')) { + if (roleLower.includes('axa') || roleLower.includes('hekem') || roleLower.includes('elder') || roleLower.includes('wise')) { return 'bg-purple-500/10 text-purple-500 border-purple-500/30'; } return 'bg-green-500/10 text-green-500 border-green-500/30'; @@ -149,7 +149,7 @@ export const NftList: React.FC = () => { Tiki #{tiki.id} - {tiki.role === 'Hemwelatî' ? 'Welati' : tiki.role} + {tiki.role} diff --git a/web/src/components/ProtectedRoute.tsx b/web/src/components/ProtectedRoute.tsx index f4863236..fff33ce3 100644 --- a/web/src/components/ProtectedRoute.tsx +++ b/web/src/components/ProtectedRoute.tsx @@ -1,23 +1,85 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Navigate } from 'react-router-dom'; import { useAuth } from '@/contexts/AuthContext'; -import { Loader2 } from 'lucide-react'; +import { usePolkadot } from '@/contexts/PolkadotContext'; +import { Loader2, Wallet } from 'lucide-react'; +import { Button } from '@/components/ui/button'; interface ProtectedRouteProps { children: React.ReactNode; requireAdmin?: boolean; } -export const ProtectedRoute: React.FC = ({ - children, - requireAdmin = false +export const ProtectedRoute: React.FC = ({ + children, + requireAdmin = false }) => { const { user, loading, isAdmin } = useAuth(); + const { selectedAccount, connectWallet } = usePolkadot(); + const [walletRestoreChecked, setWalletRestoreChecked] = useState(false); + const [forceUpdate, setForceUpdate] = useState(0); - if (loading) { + // Listen for wallet changes + useEffect(() => { + const handleWalletChange = () => { + setForceUpdate(prev => prev + 1); + }; + + window.addEventListener('walletChanged', handleWalletChange); + return () => window.removeEventListener('walletChanged', handleWalletChange); + }, []); + + // Wait for wallet restoration (max 3 seconds) + useEffect(() => { + const timeout = setTimeout(() => { + setWalletRestoreChecked(true); + }, 3000); + + // If wallet restored earlier, clear timeout + if (selectedAccount) { + setWalletRestoreChecked(true); + clearTimeout(timeout); + } + + return () => clearTimeout(timeout); + }, [selectedAccount, forceUpdate]); + + // Show loading while: + // 1. Auth is loading, OR + // 2. Wallet restoration not checked yet + if (loading || !walletRestoreChecked) { return (
- +
+ +

+ {!walletRestoreChecked ? 'Restoring wallet connection...' : 'Loading...'} +

+
+
+ ); + } + + // For admin routes, require wallet connection + if (requireAdmin && !selectedAccount) { + const handleConnect = async () => { + await connectWallet(); + // Event is automatically dispatched by handleSetSelectedAccount wrapper + }; + + return ( +
+
+ +

Connect Your Wallet

+

+ Admin panel requires wallet authentication. Please connect your wallet to continue. +

+ +
); } @@ -27,7 +89,20 @@ export const ProtectedRoute: React.FC = ({ } if (requireAdmin && !isAdmin) { - return ; + return ( +
+
+
+

Access Denied

+

+ Your wallet ({selectedAccount?.address.slice(0, 8)}...) does not have admin privileges. +

+

+ Only founder and commission members can access the admin panel. +

+
+
+ ); } return <>{children}; diff --git a/web/src/components/TokenSwap.tsx b/web/src/components/TokenSwap.tsx index 59a202bb..81a3e7e0 100644 --- a/web/src/components/TokenSwap.tsx +++ b/web/src/components/TokenSwap.tsx @@ -863,8 +863,8 @@ const TokenSwap = () => { type="number" value={fromAmount} onChange={(e) => setFromAmount(e.target.value)} - placeholder="0.0" - className="text-2xl font-bold border-0 bg-transparent text-white placeholder:text-gray-600" + placeholder="Amount" + className="text-2xl font-bold border-0 bg-transparent text-white placeholder:text-gray-500 placeholder:opacity-50" disabled={!selectedAccount} /> = ({ isOpen, onClose, s id="recipient" value={recipient} onChange={(e) => setRecipient(e.target.value)} - placeholder="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" - className="bg-gray-800 border-gray-700 text-white mt-2" + placeholder="Recipient address" + className="bg-gray-800 border-gray-700 text-white mt-2 placeholder:text-gray-500 placeholder:opacity-50" disabled={isTransferring} /> @@ -283,8 +283,8 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s step={selectedToken === 'HEZ' || selectedToken === 'PEZ' ? '0.0001' : '0.000001'} value={amount} onChange={(e) => setAmount(e.target.value)} - placeholder="0.0000" - className="bg-gray-800 border-gray-700 text-white mt-2" + placeholder="Amount" + className="bg-gray-800 border-gray-700 text-white mt-2 placeholder:text-gray-500 placeholder:opacity-50" disabled={isTransferring} />
diff --git a/web/src/components/USDTBridge.tsx b/web/src/components/USDTBridge.tsx index 5c1fc801..836b3e2d 100644 --- a/web/src/components/USDTBridge.tsx +++ b/web/src/components/USDTBridge.tsx @@ -214,8 +214,8 @@ export const USDTBridge: React.FC = ({ type="number" value={depositAmount} onChange={(e) => setDepositAmount(e.target.value)} - placeholder="0.00" - 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="Amount" + 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} />
@@ -279,9 +279,9 @@ export const USDTBridge: React.FC = ({ type="number" value={withdrawAmount} onChange={(e) => setWithdrawAmount(e.target.value)} - placeholder="0.00" + placeholder="Amount" 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" + 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} />