import { useEffect, useState, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { useAuth } from '@/contexts/AuthContext'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { supabase } from '@/lib/supabase'; import { User, Mail, Phone, Globe, MapPin, Calendar, Shield, AlertCircle, ArrowLeft, Award, Users, TrendingUp, UserMinus, Play, Loader2 } from 'lucide-react'; import { useToast } from '@/hooks/use-toast'; import { fetchUserTikis, getPrimaryRole, getTikiDisplayName, getTikiColor, getTikiEmoji, getUserRoleCategories, getAllTikiNFTDetails, generateCitizenNumber, type TikiNFTDetails } from '@pezkuwi/lib/tiki'; import { getAllScores, getStakingScoreStatus, startScoreTracking, type UserScores, type StakingScoreStatus, formatDuration } from '@pezkuwi/lib/scores'; import { web3FromAddress } from '@pezkuwi/extension-dapp'; import { getKycStatus } from '@pezkuwi/lib/kyc'; import { ReferralDashboard } from '@/components/referral/ReferralDashboard'; // Commission proposals card removed - no longer using notary system for KYC approval // import { CommissionProposalsCard } from '@/components/dashboard/CommissionProposalsCard'; export default function Dashboard() { const { user } = useAuth(); const { api, isApiReady, peopleApi, isPeopleReady, selectedAccount } = usePezkuwi(); const navigate = useNavigate(); const { toast } = useToast(); const [profile, setProfile] = useState | null>(null); const [loading, setLoading] = useState(true); const [tikis, setTikis] = useState([]); const [scores, setScores] = useState({ trustScore: 0, referralScore: 0, stakingScore: 0, tikiScore: 0, totalScore: 0 }); const [loadingScores, setLoadingScores] = useState(false); const [stakingStatus, setStakingStatus] = useState(null); const [startingScoreTracking, setStartingScoreTracking] = useState(false); const [kycStatus, setKycStatus] = useState('NotStarted'); const [renouncingCitizenship, setRenouncingCitizenship] = useState(false); const [nftDetails, setNftDetails] = useState<{ citizenNFT: TikiNFTDetails | null; roleNFTs: TikiNFTDetails[]; totalNFTs: number }>({ citizenNFT: null, roleNFTs: [], totalNFTs: 0 }); const fetchProfile = useCallback(async () => { if (!user) return; try { const { data, error } = await supabase .from('profiles') .select('*') .eq('id', user.id) .maybeSingle(); if (error) { if (import.meta.env.DEV) console.error('Profile fetch error:', error); return; } // Auto-sync user metadata from Auth to profiles if missing if (data) { const needsUpdate: Record = {}; // Sync full_name from Auth metadata if not set in profiles if (!data.full_name && user.user_metadata?.full_name) { needsUpdate.full_name = user.user_metadata.full_name; } // Sync phone from Auth metadata if not set in profiles if (!data.phone_number && user.user_metadata?.phone) { needsUpdate.phone_number = user.user_metadata.phone; } // Sync email if not set if (!data.email && user.email) { needsUpdate.email = user.email; } // If there are fields to update, update the profile if (Object.keys(needsUpdate).length > 0) { const { error: updateError } = await supabase .from('profiles') .update(needsUpdate) .eq('id', user.id); if (!updateError) { // Update local state Object.assign(data, needsUpdate); } } } // Note: Email verification is handled by Supabase Auth (user.email_confirmed_at) // We don't store it in profiles table to avoid duplication setProfile(data); } catch (error) { if (import.meta.env.DEV) console.error('Error fetching profile:', error); } finally { setLoading(false); } }, [user]); const fetchScoresAndTikis = useCallback(async () => { if (!selectedAccount || !api || !peopleApi) return; setLoadingScores(true); try { // Fetch all scores with frontend fallback (until runtime upgrade) // - Trust, referral, tiki: People Chain (on-chain) // - Staking: Relay Chain with frontend fallback const allScores = await getAllScores(peopleApi, selectedAccount.address); setScores(allScores); // Fetch staking score tracking status const stakingStatusResult = await getStakingScoreStatus(peopleApi, selectedAccount.address); setStakingStatus(stakingStatusResult); // Fetch tikis from People Chain (tiki pallet is on People Chain) const userTikis = await fetchUserTikis(peopleApi, selectedAccount.address); setTikis(userTikis); // Fetch NFT details from People Chain const details = await getAllTikiNFTDetails(peopleApi, selectedAccount.address); setNftDetails(details); // Fetch KYC status from People Chain (identityKyc pallet is on People Chain) const kycStatusResult = await getKycStatus(peopleApi, selectedAccount.address); setKycStatus(kycStatusResult); } catch (error) { if (import.meta.env.DEV) console.error('Error fetching scores and tikis:', error); } finally { setLoadingScores(false); } }, [selectedAccount, api, peopleApi]); const handleStartScoreTracking = async () => { if (!peopleApi || !selectedAccount) { toast({ title: "Hata", description: "Lütfen önce cüzdanınızı bağlayın", variant: "destructive" }); return; } setStartingScoreTracking(true); try { const injector = await web3FromAddress(selectedAccount.address); const result = await startScoreTracking(peopleApi, selectedAccount.address, injector.signer); if (result.success) { toast({ title: "Başarılı", description: "Score tracking başlatıldı! Staking score'unuz artık hesaplanacak." }); // Refresh scores after starting tracking fetchScoresAndTikis(); } else { toast({ title: "Hata", description: result.error || "Score tracking başlatılamadı", variant: "destructive" }); } } catch (error) { if (import.meta.env.DEV) console.error('Error starting score tracking:', error); toast({ title: "Hata", description: error instanceof Error ? error.message : "Score tracking başlatılamadı", variant: "destructive" }); } finally { setStartingScoreTracking(false); } }; useEffect(() => { fetchProfile(); if (selectedAccount && api && isApiReady && peopleApi && isPeopleReady) { fetchScoresAndTikis(); } }, [user, selectedAccount, api, isApiReady, peopleApi, isPeopleReady, fetchProfile, fetchScoresAndTikis]); const sendVerificationEmail = async () => { if (!user?.email) { toast({ title: "Error", description: "No email address found", variant: "destructive" }); return; } if (import.meta.env.DEV) console.log('🔄 Attempting to send verification email to:', user.email); if (import.meta.env.DEV) console.log('🔐 User object:', user); try { // Method 1: Try resend API if (import.meta.env.DEV) console.log('📧 Trying Supabase auth.resend()...'); const { error: resendError } = await supabase.auth.resend({ type: 'signup', email: user.email, }); if (resendError) { if (import.meta.env.DEV) console.error('❌ Resend error:', resendError); } else { if (import.meta.env.DEV) console.log('✅ Resend successful'); } // If resend fails, try alternative method if (resendError) { if (import.meta.env.DEV) console.warn('Resend failed, trying alternative method:', resendError); // Method 2: Request password reset as verification alternative // This will send an email if the account exists const { error: resetError } = await supabase.auth.resetPasswordForEmail(user.email, { redirectTo: `${window.location.origin}/email-verification`, }); if (resetError) throw resetError; } toast({ title: "Verification Email Sent", description: "Please check your email inbox and spam folder", }); } catch (error) { if (import.meta.env.DEV) console.error('Error sending verification email:', error); // Provide more detailed error message let errorMessage = "Failed to send verification email"; if (error.message?.includes('Email rate limit exceeded')) { errorMessage = "Too many requests. Please wait a few minutes and try again."; } else if (error.message?.includes('User not found')) { errorMessage = "Account not found. Please sign up first."; } else if (error.message) { errorMessage = error.message; } toast({ title: "Error", description: errorMessage, variant: "destructive" }); } }; const handleRenounceCitizenship = async () => { if (!api || !selectedAccount) { toast({ title: "Error", description: "Please connect your wallet first", variant: "destructive" }); return; } if (kycStatus !== 'Approved') { toast({ title: "Error", description: "Only citizens can renounce citizenship", variant: "destructive" }); return; } // Confirm action const confirmed = window.confirm( 'Are you sure you want to renounce your citizenship? This will:\n' + '• Burn your Citizen (Welati) NFT\n' + '• Reset your KYC status to NotStarted\n' + '• Remove all associated citizen privileges\n\n' + 'You can always reapply later if you change your mind.' ); if (!confirmed) return; setRenouncingCitizenship(true); try { const { web3FromAddress } = await import('@pezkuwi/extension-dapp'); const injector = await web3FromAddress(selectedAccount.address); if (import.meta.env.DEV) console.log('Renouncing citizenship...'); const tx = api.tx.identityKyc.renounceCitizenship(); await tx.signAndSend(selectedAccount.address, { signer: injector.signer }, ({ status, events, dispatchError }) => { if (dispatchError) { let errorMessage = 'Transaction failed'; if (dispatchError.isModule) { const decoded = api.registry.findMetaError(dispatchError.asModule); errorMessage = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`; } else { errorMessage = dispatchError.toString(); } if (import.meta.env.DEV) console.error(errorMessage); toast({ title: "Renunciation Failed", description: errorMessage, variant: "destructive" }); setRenouncingCitizenship(false); return; } if (status.isInBlock || status.isFinalized) { if (import.meta.env.DEV) console.log('✅ Citizenship renounced successfully'); // Check for CitizenshipRenounced event events.forEach(({ event }) => { if (event.section === 'identityKyc' && event.method === 'CitizenshipRenounced') { if (import.meta.env.DEV) console.log('📢 CitizenshipRenounced event detected'); toast({ title: "Citizenship Renounced", description: "Your citizenship has been successfully renounced. You can reapply anytime." }); // Refresh data after a short delay setTimeout(() => { fetchScoresAndTikis(); }, 2000); } }); setRenouncingCitizenship(false); } }); } catch (err) { if (import.meta.env.DEV) console.error('Renunciation error:', err); const errorMsg = err instanceof Error ? err.message : 'Failed to renounce citizenship'; toast({ title: "Error", description: errorMsg, variant: "destructive" }); setRenouncingCitizenship(false); } }; const getRoleDisplay = (): string => { if (loadingScores) return 'Loading...'; if (!selectedAccount) return 'Member'; if (tikis.length === 0) return 'Member'; const primaryRole = getPrimaryRole(tikis); return getTikiDisplayName(primaryRole); }; const getRoleCategories = (): string[] => { if (tikis.length === 0) return ['Member']; return getUserRoleCategories(tikis); }; if (loading) { return
Loading...
; } return (

User Dashboard

Account Status
{user?.email_confirmed_at || profile?.email_verified ? ( Verified ) : ( Unverified )}

{user?.email_confirmed_at ? `Verified on ${new Date(user.email_confirmed_at).toLocaleDateString()}` : 'Email verification status'}

Member Since
{new Date(profile?.joined_at || user?.created_at).toLocaleDateString()}

Registration date

Role
{getRoleDisplay()}

{selectedAccount ? 'From Tiki NFTs' : 'Connect wallet for roles'}

Total Score
{loadingScores ? '...' : scores.totalScore}

Combined from all score types

Trust Score
{loadingScores ? '...' : scores.trustScore}

Frontend calculation

Referral Score
{loadingScores ? '...' : scores.referralScore}

From referral system

Staking Score
{loadingScores ? '...' : scores.stakingScore}
{stakingStatus?.isTracking ? (

Tracking: {formatDuration(stakingStatus.durationBlocks)}

) : selectedAccount ? ( ) : (

Connect wallet to track

)}
Tiki Score
{loadingScores ? '...' : scores.tikiScore}

{tikis.length} {tikis.length === 1 ? 'role' : 'roles'} assigned

Profile Roles & Tikis Referrals Security Activity Profile Information Your personal details and contact information
Full Name: {profile?.full_name || 'Not set'}
Email: {user?.email} {user?.email_confirmed_at || profile?.email_verified ? ( Verified ) : ( )}
Recovery Email: {profile?.recovery_email || 'Not set'} {profile?.recovery_email_verified && profile?.recovery_email && ( Verified )}
Phone: {profile?.phone_number || 'Not set'}
Website: {profile?.website || 'Not set'}
Location: {profile?.location || 'Not set'}
Roles & Tikis {selectedAccount ? 'Your roles from the blockchain (Pallet-Tiki)' : 'Connect your wallet to view your roles'} {!selectedAccount && (

Connect your Pezkuwi wallet to view your on-chain roles

)} {selectedAccount && loadingScores && (

Loading roles from blockchain...

)} {selectedAccount && !loadingScores && tikis.length === 0 && (

No roles assigned yet

Complete KYC to become a Citizen (Welati)

)} {selectedAccount && !loadingScores && tikis.length > 0 && (
Primary Role: {getTikiEmoji(getPrimaryRole(tikis))} {getTikiDisplayName(getPrimaryRole(tikis))}
Total Score: {scores.totalScore}
Categories: {getRoleCategories().join(', ')}

All Roles ({tikis.length})

{tikis.map((tiki, index) => ( {getTikiEmoji(tiki)} {getTikiDisplayName(tiki)} ))}
{nftDetails.totalNFTs > 0 && (

NFT Details ({nftDetails.totalNFTs})

{nftDetails.citizenNFT && (
{nftDetails.citizenNFT.tikiEmoji} Citizen NFT Primary
{/* NFT Number and Citizen Number - Side by Side */}
{/* NFT Number */}
NFT Number:
#{nftDetails.citizenNFT.collectionId}-{nftDetails.citizenNFT.itemId}
{/* Citizen Number = NFT Number + 6 digits */}
Citizen Number:
#{nftDetails.citizenNFT.collectionId}-{nftDetails.citizenNFT.itemId}-{generateCitizenNumber( nftDetails.citizenNFT.owner, nftDetails.citizenNFT.collectionId, nftDetails.citizenNFT.itemId )}
Collection ID: {nftDetails.citizenNFT.collectionId}
Item ID: {nftDetails.citizenNFT.itemId}
Role: {nftDetails.citizenNFT.tikiDisplayName}
Tiki Type: {nftDetails.citizenNFT.tikiRole}
)} {nftDetails.roleNFTs.length > 0 && (

Additional Role NFTs:

{nftDetails.roleNFTs.map((nft, /*index*/) => (
{nft.tikiEmoji} {nft.tikiDisplayName} Score: {nft.tikiScore}
Collection: {nft.collectionId}
Item: {nft.itemId}
Tiki Type: {nft.tikiRole}
))}
)}
)}

Blockchain Address

{selectedAccount.address}

{kycStatus === 'Approved' && (

Renounce Citizenship

You can voluntarily renounce your citizenship at any time. This will:

  • Burn your Citizen (Welati) NFT
  • Reset your KYC status
  • Remove citizen privileges

Note: You can always reapply for citizenship later if you change your mind.

)}
)}
Security Settings Manage your account security

Password

Last changed: Never

{!user?.email_confirmed_at && !profile?.email_verified && (

Verify your email

Please verify your email to access all features

)}
Recent Activity Your recent actions and transactions

No recent activity

); }