Skip to content

IFIF Project Management System

A comprehensive decentralized crowdfunding platform built with IFIF protocol, demonstrating project creation, investment management, NFT operations, and investor relations.

Overview

The IFIF Project Management System showcases how to implement comprehensive investment and crowdfunding functionality using the IFIF smart contracts. It provides a complete Web3 application with progressive data loading, advanced table management with TanStack, and multiple specialized interfaces for project lifecycle management, investor relations, and user portfolio tracking.

Key Features

  • Project Portfolio Management: Browse, filter, and deploy investment projects with card/table views, advanced TanStack sorting, and deployment modals
  • Investment Client Operations: Purchase, refund, claim, and deposit functionality through specialized client modals in project detail pages
  • Project Detail & Investor Management: Comprehensive project pages with metrics grids, investor tracking tables, and transaction history
  • NFT Operations & Management: Split, merge, and convert NFT operations with dedicated project NFT sub-pages and action modals
  • Token Analytics Dashboard: Token deployment tracking, metrics visualization, and detailed token information pages
  • Activity & Transaction Tracking: System-wide activity monitoring with TanStack tables, filtering, and transaction details
  • User Portfolio Analytics: Individual user dashboards with investment tracking, activity history, and NFT portfolio management
  • Progressive Data Loading: Real-time data synchronization with optimized caching, filtering, and progressive hooks integration

Smart Contract Integration

Project Management Hooks

The system provides type-safe React hooks for all project operations:

Project Deployment

import { useDeployProject } from '@/lib/ifif-project-management-hooks'
import { DeployProjectModal } from '@/components/ifif-project-management-modals'
import { shortenHash, shortenAddress } from '@/config/constants'
import { toast } from 'sonner'
 
function DeployProjectButton() {
  const { deployProject, isLoading, isSuccess, isError, error, txHash, reset } = useDeployProject()
  
  const handleDeployProject = async () => {
    try {
      const config = {
        fundToken: '0x123...' as Address,
        projectId: 1,
        fundAmount: parseUnits('1000', 18),
        privateSaleTime: Math.floor(Date.now() / 1000) + 86400, // 1 day from now
        privateBonusPercent: 10,
        publicSaleTime: Math.floor(Date.now() / 1000) + 172800, // 2 days from now
        desiredEndEpoch: Math.floor(Date.now() / 1000) + 604800 // 1 week from now
      }
      
      const nextConfig = {
        projectOwner: '0x456...' as Address,
        distributor: '0x789...' as Address,
        distributorFeePercent: 5,
        projectOwnerFeePercent: 10,
        platformFeePercent: 2,
        pairPrice: parseUnits('1', 18)
      }
      
      const hash = await deployProject({
        config,
        nextConfig,
        configSignature: '0xsignature1...' as `0x${string}`,
        nextConfigSignature: '0xsignature2...' as `0x${string}`,
        projectName: 'Test Project',
        projectSymbol: 'TEST'
      })
      
      console.log('Transaction hash:', hash)
    } catch (err) {
      console.error('Failed to deploy project:', err)
      // Error is automatically handled by the hook state
    }
  }
 
  // Handle success state
  useEffect(() => {
    if (isSuccess && txHash) {
      toast.success(`Project deployed successfully! TX: ${shortenHash(txHash)}`)
      reset() // Reset hook state after success
    }
  }, [isSuccess, txHash, reset])
 
  // Handle error state
  useEffect(() => {
    if (isError && error) {
      toast.error(`Deployment failed: ${error}`)
    }
  }, [isError, error])
 
  return (
    <Button 
      onClick={handleDeployProject}
      disabled={isLoading}
    >
      {isLoading ? 'Deploying...' : 'Deploy Project'}
    </Button>
  )
}

Investment Operations

import { usePurchase, useRefund, useClaim, useDeposit } from '@/lib/ifif-project-management-hooks'
import { PurchaseModal, RefundModal, ClaimModal, DepositModal } from '@/components/ifif-project-client-modals'
 
function InvestmentControls({ projectAddress }: { projectAddress: Address }) {
  const { purchase, isLoading: isPurchasing, isSuccess: purchaseSuccess, isError: purchaseError, error: purchaseErrorMessage, txHash: purchaseTxHash, reset: resetPurchase } = usePurchase()
  const { refund, isLoading: isRefunding, isSuccess: refundSuccess, isError: refundError, error: refundErrorMessage, txHash: refundTxHash, reset: resetRefund } = useRefund()
  const { claim, isLoading: isClaiming, isSuccess: claimSuccess, isError: claimError, error: claimErrorMessage, txHash: claimTxHash, reset: resetClaim } = useClaim()
  const { deposit, isLoading: isDepositing, isSuccess: depositSuccess, isError: depositError, error: depositErrorMessage, txHash: depositTxHash, reset: resetDeposit } = useDeposit()
  
  const handlePurchase = async (amount: string, fundTokenAddress: Address, merkleProof: string[] = []) => {
    try {
      await purchase({
        projectAddress,
        amount: parseUnits(amount, 18),
        fundTokenAddress,
        merkleProof
      })
    } catch (err) {
      console.error('Purchase failed:', err)
      // Error state automatically handled by hook
    }
  }
  
  const handleRefund = async () => {
    try {
      await refund({ projectAddress })
    } catch (err) {
      console.error('Refund failed:', err)
      // Error state automatically handled by hook
    }
  }
  
  const handleClaim = async () => {
    try {
      await claim({ projectAddress })
    } catch (err) {
      console.error('Claim failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  const handleDeposit = async (amount: string, fundTokenAddress: Address) => {
    try {
      await deposit({
        projectAddress,
        amount: parseUnits(amount, 18),
        fundTokenAddress
      })
    } catch (err) {
      console.error('Deposit failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  // Handle success states with UI feedback
  useEffect(() => {
    if (purchaseSuccess && purchaseTxHash) {
      console.log(`Purchase successful! TX: ${purchaseTxHash}`)
      resetPurchase()
    }
    if (refundSuccess && refundTxHash) {
      console.log(`Refund successful! TX: ${refundTxHash}`)
      resetRefund()
    }
    if (claimSuccess && claimTxHash) {
      console.log(`Claim successful! TX: ${claimTxHash}`)
      resetClaim()
    }
    if (depositSuccess && depositTxHash) {
      console.log(`Deposit successful! TX: ${depositTxHash}`)
      resetDeposit()
    }
  }, [purchaseSuccess, refundSuccess, claimSuccess, depositSuccess])
 
  // Handle error states with UI feedback
  useEffect(() => {
    if (purchaseError && purchaseErrorMessage) {
      console.error(`Purchase failed: ${purchaseErrorMessage}`)
    }
    if (refundError && refundErrorMessage) {
      console.error(`Refund failed: ${refundErrorMessage}`)
    }
    if (claimError && claimErrorMessage) {
      console.error(`Claim failed: ${claimErrorMessage}`)
    }
    if (depositError && depositErrorMessage) {
      console.error(`Deposit failed: ${depositErrorMessage}`)
    }
  }, [purchaseError, refundError, claimError, depositError])
 
  return (
    <div className="space-y-2">
      <Button onClick={() => handlePurchase('100', '0x123...' as Address)} disabled={isPurchasing}>
        {isPurchasing ? 'Purchasing...' : 'Purchase Tokens'}
      </Button>
      
      <Button onClick={handleRefund} disabled={isRefunding} variant="destructive">
        {isRefunding ? 'Processing...' : 'Request Refund'}
      </Button>
      
      <Button onClick={handleClaim} disabled={isClaiming}>
        {isClaiming ? 'Claiming...' : 'Claim Tokens'}
      </Button>
 
      <Button onClick={() => handleDeposit('50', '0x123...' as Address)} disabled={isDepositing}>
        {isDepositing ? 'Depositing...' : 'Deposit Tokens'}
      </Button>
    </div>
  )
}

Project Configuration

import { 
  useUpdateProjectConfig, 
  useStartPrivateSale, 
  useStartPublicSale, 
  useEndSales, 
  usePublicEndSales 
} from '@/lib/ifif-project-management-hooks'
import { 
  UpdateProjectConfigModal,
  StartPrivateSaleModal,
  StartPublicSaleModal,
  EndSalesModal,
  PublicEndSalesModal 
} from '@/components/ifif-project-management-modals'
 
function ProjectConfigControls({ projectAddress }: { projectAddress: Address }) {
  const { updateProjectConfig, isLoading: isUpdatingConfig, isSuccess: configUpdateSuccess, isError: configUpdateError, error: configError, txHash: configTxHash, reset: resetConfig } = useUpdateProjectConfig()
  const { startPrivateSale, isLoading: isStartingPrivate, isSuccess: privateSaleSuccess, isError: privateSaleError, error: privateSaleErrorMessage, txHash: privateSaleTxHash, reset: resetPrivateSale } = useStartPrivateSale()
  const { startPublicSale, isLoading: isStartingPublic, isSuccess: publicSaleSuccess, isError: publicSaleError, error: publicSaleErrorMessage, txHash: publicSaleTxHash, reset: resetPublicSale } = useStartPublicSale()
  const { endSales, isLoading: isEndingSales, isSuccess: endSalesSuccess, isError: endSalesError, error: endSalesErrorMessage, txHash: endSalesTxHash, reset: resetEndSales } = useEndSales()
  const { publicEndSales, isLoading: isPublicEndingSales, isSuccess: publicEndSalesSuccess, isError: publicEndSalesError, error: publicEndSalesErrorMessage, txHash: publicEndSalesTxHash, reset: resetPublicEndSales } = usePublicEndSales()
  
  const handleUpdateConfig = async () => {
    try {
      const nextConfig = {
        projectOwner: '0x456...' as Address,
        distributor: '0x789...' as Address,
        distributorFeePercent: 5,
        projectOwnerFeePercent: 10,
        platformFeePercent: 2,
        pairPrice: parseUnits('1', 18)
      }
      
      await updateProjectConfig({
        projectAddress,
        nextConfig,
        signatures: ['0xsig1', '0xsig2', '0xsig3'] // [manager, distributor, owner] signatures
      })
    } catch (err) {
      console.error('Config update failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  const handleStartPrivateSale = async () => {
    try {
      await startPrivateSale({ projectAddress })
    } catch (err) {
      console.error('Private sale start failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  const handleStartPublicSale = async () => {
    try {
      await startPublicSale({ projectAddress })
    } catch (err) {
      console.error('Public sale start failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  const handleEndSales = async () => {
    try {
      await endSales({ projectAddress })
    } catch (err) {
      console.error('End sales failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  const handlePublicEndSales = async () => {
    try {
      await publicEndSales({ projectAddress })
    } catch (err) {
      console.error('Public end sales failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  // Handle success states
  useEffect(() => {
    if (configUpdateSuccess && configTxHash) {
      console.log(`Config updated successfully! TX: ${configTxHash}`)
      resetConfig()
    }
    if (privateSaleSuccess && privateSaleTxHash) {
      console.log(`Private sale started successfully! TX: ${privateSaleTxHash}`)
      resetPrivateSale()
    }
    if (publicSaleSuccess && publicSaleTxHash) {
      console.log(`Public sale started successfully! TX: ${publicSaleTxHash}`)
      resetPublicSale()
    }
    if (endSalesSuccess && endSalesTxHash) {
      console.log(`Sales ended successfully! TX: ${endSalesTxHash}`)
      resetEndSales()
    }
    if (publicEndSalesSuccess && publicEndSalesTxHash) {
      console.log(`Public end sales completed! TX: ${publicEndSalesTxHash}`)
      resetPublicEndSales()
    }
  }, [configUpdateSuccess, privateSaleSuccess, publicSaleSuccess, endSalesSuccess, publicEndSalesSuccess])
 
  // Handle error states
  useEffect(() => {
    if (configUpdateError && configError) {
      console.error(`Config update failed: ${configError}`)
    }
    if (privateSaleError && privateSaleErrorMessage) {
      console.error(`Private sale start failed: ${privateSaleErrorMessage}`)
    }
    if (publicSaleError && publicSaleErrorMessage) {
      console.error(`Public sale start failed: ${publicSaleErrorMessage}`)
    }
    if (endSalesError && endSalesErrorMessage) {
      console.error(`End sales failed: ${endSalesErrorMessage}`)
    }
    if (publicEndSalesError && publicEndSalesErrorMessage) {
      console.error(`Public end sales failed: ${publicEndSalesErrorMessage}`)
    }
  }, [configUpdateError, privateSaleError, publicSaleError, endSalesError, publicEndSalesError])
 
  return (
    <div className="space-y-2">
      <Button onClick={handleUpdateConfig} disabled={isUpdatingConfig}>
        {isUpdatingConfig ? 'Updating...' : 'Update Project Config'}
      </Button>
      
      <Button onClick={handleStartPrivateSale} disabled={isStartingPrivate}>
        {isStartingPrivate ? 'Starting...' : 'Start Private Sale'}
      </Button>
      
      <Button onClick={handleStartPublicSale} disabled={isStartingPublic}>
        {isStartingPublic ? 'Starting...' : 'Start Public Sale'}
      </Button>
 
      <Button onClick={handleEndSales} disabled={isEndingSales} variant="destructive">
        {isEndingSales ? 'Ending...' : 'End Sales'}
      </Button>
 
      <Button onClick={handlePublicEndSales} disabled={isPublicEndingSales} variant="destructive">
        {isPublicEndingSales ? 'Ending...' : 'Public End Sales'}
      </Button>
    </div>
  )
}

High-Level Project Management

import { useProjectManagement } from '@/lib/ifif-project-management-hooks'
 
function ProjectManager({ projectAddress }: { projectAddress: Address }) {
  const {
    // Deployment operations
    updateDeploymentDetails,
    isUpdatingDeploymentDetails,
    updateDeploymentDetailsSuccess,
    updateDeploymentDetailsError,
    updateDeploymentDetailsTxHash,
    
    // Configuration operations
    updateProjectConfig,
    isUpdatingProjectConfig,
    updateProjectConfigSuccess,
    updateProjectConfigError,
    updateProjectConfigTxHash,
    
    // Sales lifecycle operations
    startPrivateSale,
    isStartingPrivateSale,
    startPrivateSaleSuccess,
    startPrivateSaleError,
    startPrivateSaleTxHash,
    
    startPublicSale,
    isStartingPublicSale,
    startPublicSaleSuccess,
    startPublicSaleError,
    startPublicSaleTxHash,
    
    endSales,
    isEndingSales,
    endSalesSuccess,
    endSalesError,
    endSalesTxHash,
    
    publicEndSales,
    isPublicEndingSales,
    publicEndSalesSuccess,
    publicEndSalesError,
    publicEndSalesTxHash,
    
    // Client operations
    purchase,
    isPurchasing,
    purchaseSuccess,
    purchaseError,
    purchaseTxHash,
    
    refund,
    isRefunding,
    refundSuccess,
    refundError,
    refundTxHash,
    
    claim,
    isClaiming,
    claimSuccess,
    claimError,
    claimTxHash,
    
    claimNFT,
    isClaimingNFT,
    claimNFTSuccess,
    claimNFTError,
    claimNFTTxHash,
    
    deposit,
    isDepositing,
    depositSuccess,
    depositError,
    depositTxHash,
    
    convertNFT,
    isConvertingNFT,
    convertNFTSuccess,
    convertNFTError,
    convertNFTTxHash,
    
    // Deployment operations
    deployProject,
    isDeploying,
    deploySuccess,
    deployError,
    deployTxHash,
    
    // Combined states for project lifecycle management
    isAnyLoading,
    hasAnyError,
    lastError,
    resetAll
  } = useProjectManagement()
  
  const handleDeploymentUpdate = async () => {
    try {
      await updateDeploymentDetails({
        projectId: 1,
        projectName: 'Updated Project Name',
        projectSymbol: 'UPD'
      })
    } catch (err) {
      console.error('Deployment update failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  const handleConfigUpdate = async () => {
    try {
      const nextConfig = {
        projectOwner: '0x456...' as Address,
        distributor: '0x789...' as Address,
        distributorFeePercent: 5,
        projectOwnerFeePercent: 10,
        platformFeePercent: 2,
        pairPrice: parseUnits('1', 18)
      }
      
      await updateProjectConfig({
        projectAddress,
        nextConfig,
        signatures: ['0xsig1', '0xsig2', '0xsig3']
      })
    } catch (err) {
      console.error('Config update failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  const handleStartPrivateSale = async () => {
    try {
      await startPrivateSale({ projectAddress })
    } catch (err) {
      console.error('Private sale start failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  const handlePurchase = async () => {
    try {
      await purchase({
        projectAddress,
        amount: parseUnits('100', 18),
        fundTokenAddress: '0x123...' as Address,
        merkleProof: []
      })
    } catch (err) {
      console.error('Purchase failed:', err)
      // Error state automatically handled by hook
    }
  }
 
  // Handle success states for project lifecycle
  useEffect(() => {
    if (updateDeploymentDetailsSuccess && updateDeploymentDetailsTxHash) {
      console.log(`Deployment details updated! TX: ${updateDeploymentDetailsTxHash}`)
    }
    if (startPrivateSaleSuccess && startPrivateSaleTxHash) {
      console.log(`Private sale started! TX: ${startPrivateSaleTxHash}`)
    }
    if (purchaseSuccess && purchaseTxHash) {
      console.log(`Purchase completed! TX: ${purchaseTxHash}`)
    }
    // Additional success handlers for other operations...
  }, [updateDeploymentDetailsSuccess, startPrivateSaleSuccess, purchaseSuccess])
 
  // Handle error states for project lifecycle
  useEffect(() => {
    if (hasAnyError && lastError) {
      console.error(`Project operation failed: ${lastError}`)
    }
  }, [hasAnyError, lastError])
 
  return (
    <div className="p-4 border rounded space-y-4">
      <h3 className="text-lg font-semibold">Project Lifecycle Management</h3>
      
      {hasAnyError && (
        <Alert variant="destructive">
          <AlertTriangle className="h-4 w-4" />
          <AlertTitle>Operation Failed</AlertTitle>
          <AlertDescription>{lastError}</AlertDescription>
        </Alert>
      )}
 
      {/* Project Management Operations */}
      <div className="space-y-3">
        <div className="grid grid-cols-2 gap-2">
          <Button 
            onClick={handleDeploymentUpdate}
            disabled={isAnyLoading}
            variant="outline"
          >
            {isUpdatingDeploymentDetails ? 'Updating...' : 'Update Details'}
          </Button>
          
          <Button 
            onClick={handleConfigUpdate}
            disabled={isAnyLoading}
            variant="outline"
          >
            {isUpdatingProjectConfig ? 'Configuring...' : 'Update Config'}
          </Button>
        </div>
 
        {/* Sales Lifecycle Management */}
        <div className="grid grid-cols-2 gap-2">
          <Button 
            onClick={handleStartPrivateSale}
            disabled={isAnyLoading}
          >
            {isStartingPrivateSale ? 'Starting...' : 'Start Private Sale'}
          </Button>
          
          <Button 
            onClick={() => startPublicSale({ projectAddress })}
            disabled={isAnyLoading}
          >
            {isStartingPublicSale ? 'Starting...' : 'Start Public Sale'}
          </Button>
        </div>
 
        <div className="grid grid-cols-2 gap-2">
          <Button 
            onClick={() => endSales({ projectAddress })}
            disabled={isAnyLoading}
            variant="destructive"
          >
            {isEndingSales ? 'Ending...' : 'End Sales'}
          </Button>
          
          <Button 
            onClick={() => publicEndSales({ projectAddress })}
            disabled={isAnyLoading}
            variant="destructive"
          >
            {isPublicEndingSales ? 'Emergency End' : 'Public End Sales'}
          </Button>
        </div>
 
        {/* Client Operations */}
        <div className="grid grid-cols-3 gap-2">
          <Button 
            onClick={handlePurchase}
            disabled={isAnyLoading}
            size="sm"
          >
            {isPurchasing ? 'Buying...' : 'Purchase'}
          </Button>
          
          <Button 
            onClick={() => refund({ projectAddress })}
            disabled={isAnyLoading}
            size="sm"
            variant="outline"
          >
            {isRefunding ? 'Processing...' : 'Refund'}
          </Button>
          
          <Button 
            onClick={() => claim({ projectAddress })}
            disabled={isAnyLoading}
            size="sm"
          >
            {isClaiming ? 'Claiming...' : 'Claim'}
          </Button>
        </div>
 
        {/* Utility Actions */}
        <div className="flex justify-between pt-4 border-t">
          <Badge variant={isAnyLoading ? 'default' : 'outline'}>
            {isAnyLoading ? 'Operation in Progress...' : 'Ready'}
          </Badge>
          
          <Button 
            onClick={resetAll} 
            variant="ghost" 
            size="sm"
            disabled={isAnyLoading}
          >
            Reset All States
          </Button>
        </div>
      </div>
    </div>
  )
}

Data Layer Integration

Progressive Data Loading

The IFIF system uses progressive loading for optimal performance with large datasets:

import { 
  useProgressiveIFIFProjectsLoader,
  useProgressiveNFTOperationsLoader,
  useProgressiveIFIFDataLoader,
  useCachedIFIFData 
} from '@/lib/progressive-ifif-hooks'
 
function IFIFDashboard() {
  // Individual progressive loaders
  const {
    projects,
    isLoading: isLoadingProjects,
    progress: projectsProgress,
    total: totalProjects
  } = useProgressiveIFIFProjectsLoader()
 
  const {
    operations,
    isLoading: isLoadingOperations,
    progress: operationsProgress,
    total: totalOperations
  } = useProgressiveNFTOperationsLoader()
 
  // Main progressive data loader with all IFIF data types
  const {
    projects: allProjects,
    operations: allOperations,
    deployments,
    claims,
    purchases,
    refunds,
    deposits,
    projectsProgress: detailedProjectsProgress,
    operationsProgress: detailedOperationsProgress,
    isAnyIFIFLoading
  } = useProgressiveIFIFDataLoader()
 
  return (
    <div className="space-y-6">
      <div>
        <h3>Active Projects ({totalProjects})</h3>
        
        {/* Progress indicator for projects */}
        {isLoadingProjects && (
          <div className="mb-4">
            <div className="text-sm text-gray-600 mb-2">
              Loading projects: {projectsProgress} / {totalProjects} 
              ({detailedProjectsProgress.percentage}%)
            </div>
            <div className="w-full bg-gray-200 rounded-full h-2">
              <div 
                className="bg-blue-600 h-2 rounded-full transition-all duration-300"
                style={{ width: `${detailedProjectsProgress.percentage}%` }}
              />
            </div>
          </div>
        )}
        
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
          {projects.map((project) => (
            <ProjectCard key={project.projectId} project={project} />
          ))}
        </div>
      </div>
 
      <div>
        <h3>NFT Operations ({totalOperations})</h3>
        
        {/* Progress indicator for operations */}
        {isLoadingOperations && (
          <div className="mb-4">
            <div className="text-sm text-gray-600 mb-2">
              Loading operations: {operationsProgress} / {totalOperations}
              ({detailedOperationsProgress.percentage}%)
            </div>
            <div className="w-full bg-gray-200 rounded-full h-2">
              <div 
                className="bg-green-600 h-2 rounded-full transition-all duration-300"
                style={{ width: `${detailedOperationsProgress.percentage}%` }}
              />
            </div>
          </div>
        )}
        
        <div className="space-y-2">
          {operations.map((operation) => (
            <OperationCard key={operation.id} operation={operation} />
          ))}
        </div>
      </div>
 
      {/* Overall loading indicator */}
      {isAnyIFIFLoading && (
        <div className="fixed bottom-4 right-4 bg-white border border-gray-200 rounded-lg p-4 shadow-lg">
          <div className="flex items-center space-x-3">
            <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600" />
            <span className="text-sm text-gray-600">Loading IFIF data...</span>
          </div>
        </div>
      )}
    </div>
  )
}

Cached Data Management

import { useCachedIFIFData, useDataCacheStore } from '@/lib/progressive-ifif-hooks'
 
function ProjectAnalytics({ projectId }: { projectId: number }) {
  // Use cached IFIF data from Zustand store
  const {
    projects,
    deployments,
    claims,
    operations,
    purchases,
    refunds,
    deposits,
    isProjectsLoading,
    isDeploymentsLoading,
    isClaimsLoading,
    isOperationsLoading,
    isPurchasesLoading,
    isRefundsLoading,
    isDepositsLoading,
    projectsProgress,
    deploymentsProgress,
    claimsProgress,
    operationsProgress,
    purchasesProgress,
    refundsProgress,
    depositsProgress,
    isAnyIFIFLoading
  } = useCachedIFIFData()
 
  // Access specific cache actions for manual cache management
  const updateProgress = useDataCacheStore((state) => state.updateProgress)
  const clearCache = useDataCacheStore((state) => state.clearCache)
  const isInitialized = useDataCacheStore((state) => state.isInitialized)
 
  // Find specific project data from cached collections
  const projectData = projects.find(p => p.projectId === projectId)
  const projectOperations = operations.filter(op => 
    op.projectAddress === projectData?.id
  )
  const projectClaims = claims.filter(c => 
    c.projectAddress === projectData?.id
  )
 
  // Calculate analytics from cached data
  const analytics = useMemo(() => {
    if (!projectData) return null
 
    const totalRaised = BigInt(projectData.totalPurchase)
    const fundingProgress = BigInt(projectData.fundingTarget) > 0n 
      ? Number((totalRaised * 100n) / BigInt(projectData.fundingTarget))
      : 0
 
    return {
      totalRaised: totalRaised.toString(),
      investorCount: purchases.filter(p => p.projectAddress === projectData.id).length,
      nftCount: operations.filter(op => 
        op.projectAddress === projectData.id && op.operationType === 1
      ).length,
      completionPercent: Math.min(fundingProgress, 100),
      recentActivity: operations
        .filter(op => op.projectAddress === projectData.id)
        .slice(0, 5)
    }
  }, [projectData, purchases, operations])
 
  if (!isInitialized || isAnyIFIFLoading) {
    return (
      <div className="space-y-4">
        <div className="flex items-center justify-between">
          <h3>Project Analytics</h3>
          <div className="flex items-center space-x-2">
            <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600" />
            <span className="text-sm text-gray-600">Loading data...</span>
          </div>
        </div>
        
        {/* Progress indicators */}
        <div className="space-y-2">
          <div className="flex justify-between text-sm">
            <span>Projects: {projectsProgress}%</span>
            <span>Operations: {operationsProgress}%</span>
          </div>
          <div className="w-full bg-gray-200 rounded-full h-2">
            <div 
              className="bg-blue-600 h-2 rounded-full transition-all duration-300"
              style={{ width: `${(projectsProgress + operationsProgress) / 2}%` }}
            />
          </div>
        </div>
        
        <LoadingSpinner />
      </div>
    )
  }
 
  if (!analytics) {
    return (
      <Alert variant="destructive">
        <AlertTriangle className="h-4 w-4" />
        <AlertTitle>Project Not Found</AlertTitle>
        <AlertDescription>
          Project with ID {projectId} was not found in cached data.
        </AlertDescription>
      </Alert>
    )
  }
 
  return (
    <div className="space-y-4">
      <div className="flex justify-between items-center">
        <h3>Project Analytics</h3>
        <div className="flex items-center space-x-2">
          <Badge variant="outline" className="text-green-600">
            Data Cached ({projects.length} projects)
          </Badge>
          <Button 
            onClick={clearCache} 
            variant="outline" 
            size="sm"
            className="text-red-600 hover:text-red-700"
          >
            Clear Cache
          </Button>
        </div>
      </div>
      
      <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
        <MetricCard 
          title="Total Raised" 
          value={formatEther(analytics.totalRaised)} 
          suffix="ETH"
        />
        <MetricCard 
          title="Investors" 
          value={analytics.investorCount} 
        />
        <MetricCard 
          title="NFTs Minted" 
          value={analytics.nftCount} 
        />
        <MetricCard 
          title="Completion" 
          value={`${analytics.completionPercent}%`} 
        />
      </div>
 
      {/* Recent activity from cached data */}
      <div className="space-y-2">
        <h4 className="text-sm font-medium">Recent Activity</h4>
        {analytics.recentActivity.map((activity) => (
          <div key={activity.id} className="text-xs text-gray-600 p-2 bg-gray-50 rounded">
            Operation {activity.operationType} - {activity.user.slice(0, 8)}...
          </div>
        ))}
      </div>
    </div>
  )
}

Project History Tracking

import { 
  useCachedIFIFData, 
  useCachedFactoryData 
} from '@/lib/progressive-ifif-hooks'
import { 
  getActivityType, 
  getActivityUser, 
  getActivityAmount,
  getActivityProjectWithLookup,
  getActivityProperty,
  type ActivityData
} from '@/lib/activity-utils'
import { getActivityTypeIndicatorColor } from '@/config/constants'
import { useReactTable, getCoreRowModel, getPaginationRowModel, getSortedRowModel, getFilteredRowModel, createColumnHelper, flexRender } from '@tanstack/react-table'
 
function IFIFActivityTracker() {
  // Get all cached IFIF data types
  const { 
    deployments,
    claims,
    operations,
    purchases,
    refunds,
    deposits,
    isOperationsLoading,
    isClaimsLoading
  } = useCachedIFIFData()
 
  // Get factory data for project name lookup
  const { projectLookup } = useCachedFactoryData()
 
  // State for filtering and table management
  const [globalFilter, setGlobalFilter] = useState('')
  const [activityFilters, setActivityFilters] = useState({
    activityType: 'all' as string,
  })
  const [sorting, setSorting] = useState<SortingState>([])
  const [pagination, setPagination] = useState<PaginationState>({
    pageIndex: 0,
    pageSize: 10,
  })
 
  // Combine all activities into unified structure
  const allActivities = useMemo(() => {
    const activities: ActivityData[] = []
    
    if (deployments) activities.push(...deployments.map(d => ({ ...d, type: 'deployment' })))
    if (claims) activities.push(...claims.map(c => ({ ...c, type: 'claim' })))
    if (operations) activities.push(...operations.map(o => ({ ...o, type: 'operation' })))
    if (purchases) activities.push(...purchases.map(p => ({ ...p, type: 'purchase' })))
    if (refunds) activities.push(...refunds.map(r => ({ ...r, type: 'refund' })))
    if (deposits) activities.push(...deposits.map(d => ({ ...d, type: 'deposit' })))
    
    // Sort by timestamp descending (newest first)
    return activities.sort((a, b) => {
      const timestampA = typeof a.timestamp === 'string' ? parseFloat(a.timestamp) : Number(a.timestamp)
      const timestampB = typeof b.timestamp === 'string' ? parseFloat(b.timestamp) : Number(b.timestamp)
      return timestampB - timestampA
    })
  }, [deployments, claims, operations, purchases, refunds, deposits])
 
  // Create table columns using activity utilities
  const columns = useMemo(() => {
    const columnHelper = createColumnHelper<ActivityData>()
    
    return [
      columnHelper.accessor('type', {
        header: 'Activity Type',
        cell: ({ row }) => {
          const activity = row.original
          const activityType = getActivityType(activity)
          const typeColor = getActivityTypeIndicatorColor(activityType)
          
          return (
            <div className="flex items-center space-x-3">
              <div className={`w-3 h-3 ${typeColor} rounded-full`} />
              <div>
                <p className="font-medium text-slate-900">{activityType}</p>
              </div>
            </div>
          )
        }
      }),
      columnHelper.accessor((row) => getActivityProjectWithLookup(row, projectLookup), {
        id: 'project',
        header: 'Project',
        cell: ({ getValue }) => {
          const projectInfo = getValue()
          if (!projectInfo) {
            return <span className="text-slate-400">-</span>
          }
          
          const { id, name, address } = projectInfo
          
          return (
            <div>
              <p className="font-medium text-slate-900">#{id} - {name}</p>
              {address && (
                <p className="text-xs text-slate-500 font-mono">{shortenHash(address)}</p>
              )}
            </div>
          )
        }
      }),
      columnHelper.accessor((row) => getActivityUser(row), {
        id: 'user',
        header: 'User',
        cell: ({ getValue }) => {
          const user = getValue()
          if (!user) {
            return <span className="text-slate-400">System</span>
          }
          return (
            <div>
              <p className="font-medium text-slate-900">{shortenHash(user)}</p>
              <p className="text-xs text-slate-500 font-mono">{user}</p>
            </div>
          )
        }
      }),
      columnHelper.accessor((row) => getActivityAmount(row), {
        id: 'amount',
        header: 'Amount',
        cell: ({ getValue }) => {
          const amount = getValue()
          return amount ? (
            <Badge variant="outline" className="font-mono">
              {Number(amount).toFixed(4)} ETH
            </Badge>
          ) : (
            <span className="text-slate-400">-</span>
          )
        }
      }),
      columnHelper.accessor('timestamp', {
        header: 'Timestamp',
        sortingFn: 'basic',
        cell: ({ getValue }) => {
          const timestamp = getValue()
          const timestampValue = typeof timestamp === 'string' ? parseFloat(timestamp) : Number(timestamp)
          const date = new Date(timestampValue * 1000)
          return (
            <div>
              <p className="font-medium text-slate-900">
                {format(date, 'MMM dd, yyyy')}
              </p>
              <p className="text-xs text-slate-500">
                {format(date, 'HH:mm:ss')}
              </p>
            </div>
          )
        }
      }),
      columnHelper.display({
        id: 'actions',
        header: 'Actions',
        cell: ({ row }) => (
          <Button
            variant="outline"
            size="sm"
            onClick={() => {
              // Open activity details modal
              setSelectedActivity(row.original)
            }}
          >
            <Eye className="h-3 w-3 mr-1" />
            View
          </Button>
        )
      })
    ]
  }, [projectLookup])
 
  // Filter activities based on type selection
  const filteredActivities = useMemo(() => {
    if (activityFilters.activityType === 'all') {
      return allActivities
    }
    return allActivities.filter(activity => activity.type === activityFilters.activityType)
  }, [allActivities, activityFilters.activityType])
 
  // Create table instance with comprehensive filtering
  const table = useReactTable({
    data: filteredActivities,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    globalFilterFn: (row, columnId, value) => {
      if (!value) return true
      
      const activity = row.original
      const searchValue = String(value).toLowerCase()
      const user = getActivityUser(activity)
      const tokenAddress = getActivityProperty(activity, 'tokenAddress')
      const activityType = getActivityType(activity)
      const projectInfo = getActivityProjectWithLookup(activity, projectLookup)
      
      return Boolean(
        (user && user.toLowerCase().includes(searchValue)) ||
        (activity.id && activity.id.toLowerCase().includes(searchValue)) ||
        (typeof tokenAddress === 'string' && tokenAddress.toLowerCase().includes(searchValue)) ||
        activityType.toLowerCase().includes(searchValue) ||
        (projectInfo && (
          (projectInfo.id && projectInfo.id.toString().toLowerCase().includes(searchValue)) ||
          (projectInfo.name && projectInfo.name.toLowerCase().includes(searchValue))
        ))
      )
    },
    onSortingChange: setSorting,
    onGlobalFilterChange: setGlobalFilter,
    onPaginationChange: setPagination,
    state: {
      sorting,
      globalFilter,
      pagination,
    },
  })
 
  // Activity metrics from filtered data
  const metrics = useMemo(() => {
    const filteredData = table.getFilteredRowModel().rows.map(row => row.original)
    const totalActivities = filteredData.length
    const last24h = filteredData.filter(a => {
      const now = Date.now() / 1000
      const activityTimestamp = typeof a.timestamp === 'string' ? parseFloat(a.timestamp) : Number(a.timestamp)
      return now - activityTimestamp <= 86400
    }).length
    
    const uniqueUsers = new Set(
      filteredData
        .map(a => getActivityUser(a))
        .filter(u => u !== null)
    ).size
 
    return {
      totalActivities,
      last24h,
      uniqueUsers
    }
  }, [table.getFilteredRowModel().rows])
 
  const isLoading = isOperationsLoading || isClaimsLoading
 
  return (
    <div className="space-y-6">
      {/* Activity Metrics */}
      <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
        <div className="bg-white p-4 border border-gray-200 rounded-lg">
          <div className="text-2xl font-bold text-gray-900">{metrics.totalActivities}</div>
          <div className="text-sm text-gray-600">Total Activities</div>
        </div>
        <div className="bg-white p-4 border border-gray-200 rounded-lg">
          <div className="text-2xl font-bold text-gray-900">{metrics.last24h}</div>
          <div className="text-sm text-gray-600">Last 24 Hours</div>
        </div>
        <div className="bg-white p-4 border border-gray-200 rounded-lg">
          <div className="text-2xl font-bold text-gray-900">{metrics.uniqueUsers}</div>
          <div className="text-sm text-gray-600">Unique Users</div>
        </div>
      </div>
 
      {/* Filters */}
      <div className="bg-white border border-gray-200 p-6">
        <div className="flex items-center justify-between">
          <div className="flex items-center space-x-4 flex-1">
            <div className="relative flex-1 max-w-md">
              <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
              <Input
                placeholder="Search activities..."
                value={table.getState().globalFilter ?? ''}
                onChange={(e) => table.setGlobalFilter(e.target.value)}
                className="pl-10"
              />
            </div>
            
            <Select 
              value={activityFilters.activityType} 
              onValueChange={(value) => setActivityFilters(prev => ({ ...prev, activityType: value }))}
            >
              <SelectTrigger className="w-48">
                <SelectValue placeholder="Filter by type" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All Types</SelectItem>
                <SelectItem value="deployment">Token Deployments</SelectItem>
                <SelectItem value="claim">Token Claims</SelectItem>
                <SelectItem value="operation">NFT Operations</SelectItem>
                <SelectItem value="purchase">User Purchases</SelectItem>
                <SelectItem value="refund">User Refunds</SelectItem>
                <SelectItem value="deposit">Asset Deposits</SelectItem>
              </SelectContent>
            </Select>
          </div>
          
          <div className="text-sm text-gray-600">
            {table.getFilteredRowModel().rows.length} activities found
          </div>
        </div>
      </div>
 
      {/* Activities Table */}
      <div className="bg-white border border-gray-200">
        {isLoading ? (
          <div className="p-6">
            <div className="space-y-4">
              {[...Array(5)].map((_, i) => (
                <div key={i} className="flex items-center space-x-4 p-4 animate-pulse">
                  <div className="w-3 h-3 bg-gray-300 rounded-full" />
                  <div className="flex-1 space-y-2">
                    <div className="h-4 bg-gray-300 w-1/4" />
                    <div className="h-3 bg-gray-200 w-1/2" />
                  </div>
                </div>
              ))}
            </div>
          </div>
        ) : table.getFilteredRowModel().rows.length > 0 ? (
          <>
            <Table>
              <TableHeader>
                {table.getHeaderGroups().map((headerGroup) => (
                  <TableRow key={headerGroup.id}>
                    {headerGroup.headers.map((header) => (
                      <TableHead 
                        key={header.id}
                        className={header.column.getCanSort() ? 'cursor-pointer select-none hover:bg-muted/50' : ''}
                        onClick={header.column.getToggleSortingHandler()}
                      >
                        <div className="flex items-center space-x-2">
                          {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
                          {header.column.getCanSort() && (
                            <div className="ml-2">
                              {header.column.getIsSorted() === 'asc' && <ChevronUp className="h-4 w-4" />}
                              {header.column.getIsSorted() === 'desc' && <ChevronDown className="h-4 w-4" />}
                              {!header.column.getIsSorted() && <ChevronsUpDown className="h-4 w-4 opacity-50" />}
                            </div>
                          )}
                        </div>
                      </TableHead>
                    ))}
                  </TableRow>
                ))}
              </TableHeader>
              <TableBody>
                {table.getRowModel().rows.map((row) => (
                  <TableRow key={row.id}>
                    {row.getVisibleCells().map((cell) => (
                      <TableCell key={cell.id}>
                        {flexRender(cell.column.columnDef.cell, cell.getContext())}
                      </TableCell>
                    ))}
                  </TableRow>
                ))}
              </TableBody>
            </Table>
 
            {/* Pagination */}
            <div className="flex items-center justify-between p-6 border-t border-gray-200">
              <div className="text-sm text-gray-600">
                Showing {table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1} to{' '}
                {Math.min(
                  (table.getState().pagination.pageIndex + 1) * table.getState().pagination.pageSize,
                  table.getFilteredRowModel().rows.length
                )}{' '}
                of {table.getFilteredRowModel().rows.length} results
              </div>
              <div className="flex space-x-2">
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => table.previousPage()}
                  disabled={!table.getCanPreviousPage()}
                >
                  Previous
                </Button>
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => table.nextPage()}
                  disabled={!table.getCanNextPage()}
                >
                  Next
                </Button>
              </div>
            </div>
          </>
        ) : (
          <div className="text-center py-12">
            <Activity className="mx-auto h-16 w-16 text-gray-300 mb-4" />
            <h3 className="text-lg font-medium text-gray-900 mb-2">No activities found</h3>
            <p className="text-gray-600">
              {table.getState().globalFilter ? 'Try adjusting your search criteria' : 'No activities recorded yet'}
            </p>
          </div>
        )}
      </div>
    </div>
  )
}

Project Metrics Dashboard

import { 
  useProjectDetailData, 
  useProjectMetrics, 
  useProjectInvestorData,
  useCachedIFIFData,
  useCachedFactoryData 
} from '@/lib/progressive-ifif-hooks'
import { MetricGrid, BarChartComponent, DonutChartComponent } from '@/components/shared'
 
// Main Projects Listing Page - Overview Metrics
function IFIFProjectsMetrics() {
  const { projects, isProjectsLoading } = useCachedIFIFData()
  const { projectLookup } = useCachedFactoryData()
 
  // Calculate aggregate metrics across all projects
  const metrics = useMemo(() => {
    if (!projects) return {
      totalProjects: 0,
      activeProjects: 0,
      completedProjects: 0,
      totalFunding: '0',
      successRate: 0,
      averageFunding: '0'
    }
 
    const totalProjects = projects.length
    const activeProjects = projects.filter(p => p.stage === 2 || p.stage === 3).length // PRIVATE_SALE=2, PUBLIC_SALE=3
    const completedProjects = projects.filter(p => p.stage === 4).length // SALE_SUCCESSED=4
    const totalFunding = projects.reduce((sum, p) => sum + Number(p.totalPurchase || 0), 0)
    const successRate = totalProjects > 0 ? (completedProjects / totalProjects) * 100 : 0
    const averageFunding = totalProjects > 0 ? totalFunding / totalProjects : 0
 
    return {
      totalProjects,
      activeProjects,
      completedProjects,
      totalFunding: Number(formatEther(BigInt(totalFunding))).toFixed(2),
      successRate: Math.round(successRate),
      averageFunding: Number(formatEther(BigInt(Math.round(averageFunding)))).toFixed(2)
    }
  }, [projects])
 
  // Chart data for project stage distribution
  const stageDistribution = useMemo(() => {
    if (!projects) return []
    
    const stageNumbers = [0, 1, 2, 3, 4, 5, 6] // NONE, INIT, PRIVATE_SALE, PUBLIC_SALE, SALE_SUCCESSED, SALE_FAILED, CLAIM
    return stageNumbers.map(stageNum => ({
      name: PROJECT_STAGES[stageNum]?.name || `Stage ${stageNum}`,
      value: projects.filter(p => p.stage === stageNum).length,
      color: PROJECT_STAGES[stageNum]?.color || '#64748b'
    })).filter(item => item.value > 0)
  }, [projects])
 
  // Funding progress distribution
  const fundingProgress = useMemo(() => {
    if (!projects) return []
    
    const ranges = [
      { name: '0-25%', min: 0, max: 0.25 },
      { name: '25-50%', min: 0.25, max: 0.5 },
      { name: '50-75%', min: 0.5, max: 0.75 },
      { name: '75-100%', min: 0.75, max: 1 },
      { name: '100%+', min: 1, max: Infinity }
    ]
 
    return ranges.map(range => ({
      name: range.name,
      value: projects.filter(p => {
        const ratio = p.totalPurchase && p.fundingTarget 
          ? Number(p.totalPurchase) / Number(p.fundingTarget) 
          : 0
        return ratio >= range.min && ratio < range.max
      }).length
    })).filter(item => item.value > 0)
  }, [projects])
 
  return (
    <div className="space-y-6">
      {/* Overview Metrics Grid */}
      <MetricGrid
        metrics={[
          {
            key: 'totalProjects',
            value: metrics.totalProjects,
            subtitle: 'Total Projects'
          },
          {
            key: 'activeProjects',
            value: metrics.activeProjects,
            subtitle: 'Active Projects'
          },
          {
            key: 'completedProjects',
            value: metrics.completedProjects,
            subtitle: 'Completed Projects'
          },
          {
            key: 'totalFunding',
            value: `${metrics.totalFunding}`,
            subtitle: 'Total Funding'
          }
        ]}
        isLoading={isProjectsLoading}
      />
 
      {/* Charts Section */}
      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
        <div className="bg-white p-6 border border-slate-200">
          <h3 className="text-lg font-semibold mb-4">Project Stage Distribution</h3>
          <DonutChartComponent 
            data={stageDistribution}
            category="value"
            dataKey="name"
            showLabel={true}
            valueFormatter={(value) => `${value} project${value !== 1 ? 's' : ''}`}
          />
        </div>
        <div className="bg-white p-6 border border-slate-200">
          <h3 className="text-lg font-semibold mb-4">Funding Progress Distribution</h3>
          <BarChartComponent 
            data={fundingProgress}
            dataKey="name"
            categories={['value']}
          />
        </div>
      </div>
    </div>
  )
}
 
// Individual Project Detail Page - Comprehensive Dashboard
function ProjectDetailDashboard({ projectId }: { projectId: number }) {
  // Get comprehensive project details and metrics
  const { project, overview, isLoading, error } = useProjectDetailData(projectId)
  const metrics = useProjectMetrics(projectId)
  const { projectLookup } = useCachedFactoryData()
 
  if (isLoading) {
    return (
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
        <div className="lg:col-span-2 space-y-6">
          <div className="bg-white border border-slate-200 space-y-4">
            <div className="p-6">
              <div className="space-y-4">
                <div className="h-6 w-48 bg-muted rounded animate-pulse" />
                <div className="h-4 w-full bg-muted rounded animate-pulse" />
                <div className="h-4 w-3/4 bg-muted rounded animate-pulse" />
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
 
  if (error || !project || !overview) {
    return (
      <Alert variant="destructive">
        <AlertTriangle className="h-4 w-4" />
        <AlertTitle>Failed to Load Project Data</AlertTitle>
        <AlertDescription>
          Project with ID {projectId} could not be loaded.
        </AlertDescription>
      </Alert>
    )
  }
 
  const projectInfo = projectLookup.get(overview.projectId.toString())
 
  return (
    <div className="space-y-6">
      {/* Header with project identification */}
      <div className="space-y-2">
        <h1 className="text-4xl font-bold tracking-tight text-slate-900 flex items-center gap-3">
          <TrendingUp className="h-8 w-8" />
          #{overview.projectId}{projectInfo?.name ? ` - ${projectInfo.name}` : ''}
        </h1>
        <div className="flex items-center gap-4">
          <StageBadge stage={overview.stage} />
          <ExplorerLink 
            hash={overview.projectAddress}
            type="address"
            variant="button"
            className="flex items-center gap-2"
          >
            <ExternalLink className="h-4 w-4" />
            View on Explorer
          </ExplorerLink>
        </div>
      </div>
 
      {/* Key Metrics Grid */}
      <MetricGrid 
        metrics={[
          {
            key: 'users' as const,
            value: overview.investorCount,
            subtitle: 'Total investors'
          },
          {
            key: 'totalFunding' as const,
            value: `${overview.fundingProgress.toFixed(1)}%`,
            subtitle: 'Funding progress'
          },
          {
            key: 'activities' as const,
            value: overview.activeNFTCount,
            subtitle: 'Active NFTs'
          },
          {
            key: 'last24h' as const,
            value: overview.totalTransactions,
            subtitle: 'Total transactions'
          }
        ]}
        isLoading={isLoading}
        columns={4}
      />
 
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
        {/* Main Project Status Card */}
        <div className="lg:col-span-2">
          <div className="bg-white border border-slate-200 space-y-6 p-6">
            <div className="flex items-center justify-between">
              <h2 className="text-xl font-semibold">Project Status</h2>
              <StageBadge stage={overview.stage} />
            </div>
            
            {/* Funding Progress */}
            <div className="space-y-4">
              <div className="flex items-center justify-between">
                <h3 className="font-semibold flex items-center gap-2">
                  <DollarSign className="h-4 w-4" />
                  Funding Progress
                </h3>
                <span className="text-sm text-muted-foreground">
                  {overview.fundingProgress.toFixed(1)}% complete
                </span>
              </div>
              <Progress value={overview.fundingProgress} className="h-3" />
              <div className="flex justify-between text-sm">
                <span className="text-muted-foreground">
                  Raised: {Number(formatEther(overview.totalPurchase)).toFixed(2)} ETH
                </span>
                <span className="text-muted-foreground">
                  Target: {Number(formatEther(overview.fundingTarget)).toFixed(2)} ETH
                </span>
              </div>
              {overview.isFullyFunded && (
                <Badge variant="outline" className="w-full justify-center bg-green-50 text-green-700 border-green-200">
                  <Target className="h-3 w-3 mr-1" />
                  Funding Target Reached
                </Badge>
              )}
            </div>
 
            {/* Project Details Grid */}
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4 pt-4 border-t">
              <div className="space-y-2">
                <label className="text-sm font-medium text-muted-foreground">Created</label>
                <div className="flex items-center gap-2">
                  <Calendar className="h-3 w-3 text-muted-foreground" />
                  <span className="text-sm">
                    {format(new Date(Number(overview.createdAt) * 1000), 'MMM dd, yyyy')}
                  </span>
                </div>
              </div>
              <div className="space-y-2">
                <label className="text-sm font-medium text-muted-foreground">Last Updated</label>
                <div className="flex items-center gap-2">
                  <Clock className="h-3 w-3 text-muted-foreground" />
                  <span className="text-sm">
                    {format(new Date(Number(overview.lastUpdated) * 1000), 'MMM dd, yyyy')}
                  </span>
                </div>
              </div>
            </div>
          </div>
        </div>
 
        {/* Sidebar with Quick Stats and Status */}
        <div className="space-y-6">
          {/* Quick Stats */}
          <div className="bg-white border border-slate-200 space-y-4">
            <div className="p-6 pb-0">
              <h2 className="text-lg font-semibold">Quick Stats</h2>
            </div>
            <div className="px-6 pb-6 space-y-4">
              <div className="grid grid-cols-2 gap-4">
                <div className="text-center space-y-1">
                  <div className="flex items-center justify-center">
                    <Users className="h-4 w-4 text-blue-500 mr-1" />
                    <span className="text-2xl font-bold">{overview.investorCount}</span>
                  </div>
                  <p className="text-xs text-muted-foreground">Investors</p>
                </div>
                <div className="text-center space-y-1">
                  <div className="flex items-center justify-center">
                    <Coins className="h-4 w-4 text-purple-500 mr-1" />
                    <span className="text-2xl font-bold">{overview.activeNFTCount}</span>
                  </div>
                  <p className="text-xs text-muted-foreground">Active NFTs</p>
                </div>
                <div className="text-center space-y-1">
                  <div className="flex items-center justify-center">
                    <Zap className="h-4 w-4 text-orange-500 mr-1" />
                    <span className="text-2xl font-bold">{overview.totalTransactions}</span>
                  </div>
                  <p className="text-xs text-muted-foreground">Transactions</p>
                </div>
                <div className="text-center space-y-1">
                  <div className="flex items-center justify-center">
                    <BarChart3 className="h-4 w-4 text-green-500 mr-1" />
                    <span className="text-2xl font-bold">{overview.recentStageChanges}</span>
                  </div>
                  <p className="text-xs text-muted-foreground">Stage Changes</p>
                </div>
              </div>
            </div>
          </div>
 
          {/* Status Indicators */}
          <div className="bg-white border border-slate-200 space-y-4">
            <div className="p-6 pb-0">
              <h2 className="text-lg font-semibold">Status Indicators</h2>
            </div>
            <div className="px-6 pb-6 space-y-2">
              <div className="flex items-center justify-between">
                <span className="text-sm">Funding Status</span>
                <Badge variant={overview.isFullyFunded ? "default" : "secondary"}>
                  {overview.isFullyFunded ? "Complete" : "In Progress"}
                </Badge>
              </div>
              <div className="flex items-center justify-between">
                <span className="text-sm">Project Activity</span>
                <Badge variant={overview.isActive ? "default" : "secondary"}>
                  {overview.isActive ? "Active" : "Inactive"}
                </Badge>
              </div>
              <div className="flex items-center justify-between">
                <span className="text-sm">Token Deployment</span>
                <Badge variant={overview.hasTokenDeployment ? "default" : "secondary"}>
                  {overview.hasTokenDeployment ? "Deployed" : "Pending"}
                </Badge>
              </div>
              <div className="flex items-center justify-between">
                <span className="text-sm">Recent Activity</span>
                <Badge variant={overview.hasRecentActivity ? "default" : "secondary"}>
                  {overview.hasRecentActivity ? "Active" : "Quiet"}
                </Badge>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
 
// Project Investor Dashboard - Investment-focused Metrics
function ProjectInvestorMetrics({ projectId }: { projectId: string }) {
  const { project } = useProjectDetailData(parseInt(projectId) || 0)
  const { 
    investors, 
    purchases, 
    refunds, 
    claims, 
    isLoading 
  } = useProjectInvestorData(project?.id || '')
 
  // Calculate investment-specific metrics
  const investorMetrics = useMemo(() => {
    const totalInvestors = investors.length
    const totalInvested = purchases.reduce((sum, p) => sum + BigInt(p.amount), BigInt(0))
    const totalRefunded = refunds.reduce((sum, r) => sum + BigInt(r.amount), BigInt(0))
    const totalClaimed = claims.reduce((sum, c) => sum + BigInt(c.amount), BigInt(0))
    const netInvestment = totalInvested - totalRefunded
 
    return [
      {
        key: 'users' as const,
        value: totalInvestors,
        subtitle: 'Total investors'
      },
      {
        key: 'totalFunding' as const,
        value: parseFloat(formatEther(totalInvested)).toFixed(2),
        subtitle: 'Total invested'
      },
      {
        key: 'activeProjects' as const,
        value: parseFloat(formatEther(netInvestment)).toFixed(2),
        subtitle: 'Net investment'
      },
      {
        key: 'activities' as const,
        value: parseFloat(formatEther(totalClaimed)).toFixed(2),
        subtitle: 'Total claimed'
      }
    ]
  }, [investors, purchases, refunds, claims])
 
  return (
    <div className="space-y-6">
      {/* Investment Metrics */}
      <MetricGrid
        metrics={investorMetrics}
        isLoading={isLoading}
        columns={4}
      />
 
      {/* Investment Status Distribution */}
      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
        <div className="bg-white p-6 border border-slate-200">
          <h3 className="text-lg font-semibold mb-4">Investment Status Distribution</h3>
          <DonutChartComponent 
            data={[
              { name: 'Active', value: investors.filter(i => i.investmentStatus === 'active').length },
              { name: 'Refunded', value: investors.filter(i => i.investmentStatus === 'refunded').length },
              { name: 'Claimed', value: investors.filter(i => i.investmentStatus === 'claimed').length }
            ].filter(item => item.value > 0)}
            category="value"
            dataKey="name"
            showLabel={true}
            valueFormatter={(value) => `${value} investor${value !== 1 ? 's' : ''}`}
          />
        </div>
        
        <div className="bg-white p-6 border border-slate-200">
          <h3 className="text-lg font-semibold mb-4">Transaction Timeline</h3>
          <BarChartComponent 
            data={[
              { name: 'Purchases', value: purchases.length },
              { name: 'Refunds', value: refunds.length },
              { name: 'Claims', value: claims.length }
            ]}
            dataKey="name"
            categories={['value']}
          />
        </div>
      </div>
    </div>
  )
}

User Interface Patterns

Project Management Modals

The IFIF system includes comprehensive modal components for project management operations:

import { 
  DeployProjectModal,
  UpdateDeploymentDetailsModal,
  UpdateProjectConfigModal,
  StartPrivateSaleModal,
  StartPublicSaleModal,
  EndSalesModal,
  PublicEndSalesModal
} from '@/components/ifif-project-management-modals'
import { 
  useSignatureHelper,
  FloatingSignatureHelper 
} from '@/lib/signature-store'
 
function ProjectManagementInterface({ 
  projectAddress, 
  projectId, 
  projectName,
  currentConfig 
}: { 
  projectAddress?: Address
  projectId?: number
  projectName?: string
  currentConfig?: {
    projectOwner: Address
    distributor: Address
    distributorFeePercent: number
    projectOwnerFeePercent: number
    platformFeePercent: number
    pairPrice: bigint
  }
}) {
  const [activeModal, setActiveModal] = useState<string | null>(null)
 
  // Signature helper for pre-signed configurations
  const { openModal: openSignatureModal } = useSignatureHelper()
 
  return (
    <div className="space-y-6">
      {/* Action Grid */}
      <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4">
        <Button 
          onClick={() => setActiveModal('deploy')}
          className="h-24 flex flex-col items-center justify-center"
        >
          <FileText className="h-6 w-6 mb-2" />
          Deploy Project
        </Button>
        
        <Button 
          onClick={() => setActiveModal('updateDetails')}
          className="h-24 flex flex-col items-center justify-center"
          variant="outline"
        >
          <Edit3 className="h-6 w-6 mb-2" />
          Update Details
        </Button>
        
        <Button 
          onClick={() => setActiveModal('configure')}
          className="h-24 flex flex-col items-center justify-center"
          variant="outline"
        >
          <Settings className="h-6 w-6 mb-2" />
          Update Config
        </Button>
        
        <Button 
          onClick={openSignatureModal}
          className="h-24 flex flex-col items-center justify-center"
          variant="outline"
        >
          <PenTool className="h-6 w-6 mb-2" />
          Signature Helper
        </Button>
      </div>
 
      {/* Sale Management Grid */}
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
        <Button 
          onClick={() => setActiveModal('privateSale')}
          className="h-20 flex flex-col items-center justify-center"
          variant="outline"
        >
          <Users className="h-5 w-5 mb-1" />
          Start Private Sale
        </Button>
        
        <Button 
          onClick={() => setActiveModal('publicSale')}
          className="h-20 flex flex-col items-center justify-center"
          variant="outline"
        >
          <Globe className="h-5 w-5 mb-1" />
          Start Public Sale
        </Button>
        
        <Button 
          onClick={() => setActiveModal('endSales')}
          className="h-20 flex flex-col items-center justify-center"
          variant="outline"
        >
          <Square className="h-5 w-5 mb-1" />
          End Sales
        </Button>
        
        <Button 
          onClick={() => setActiveModal('publicEnd')}
          className="h-20 flex flex-col items-center justify-center"
          variant="outline"
        >
          <Clock className="h-5 w-5 mb-1" />
          Emergency End
        </Button>
      </div>
 
      {/* Deploy Project Modal */}
      <DeployProjectModal
        isOpen={activeModal === 'deploy'}
        onClose={() => setActiveModal(null)}
        onSuccess={() => {
          console.log('Project deployed successfully')
          setActiveModal(null)
        }}
      />
      
      {/* Update Deployment Details Modal */}
      <UpdateDeploymentDetailsModal
        isOpen={activeModal === 'updateDetails'}
        onClose={() => setActiveModal(null)}
        projectId={projectId}
        currentName={projectName}
        currentSymbol="" // Would need to be provided from props
        onSuccess={() => {
          console.log('Project details updated')
          setActiveModal(null)
        }}
      />
      
      {/* Update Project Config Modal */}
      <UpdateProjectConfigModal
        isOpen={activeModal === 'configure'}
        onClose={() => setActiveModal(null)}
        projectAddress={projectAddress}
        projectName={projectName}
        currentConfig={currentConfig}
        onSuccess={() => {
          console.log('Project config updated')
          setActiveModal(null)
        }}
      />
      
      {/* Start Private Sale Modal */}
      <StartPrivateSaleModal
        isOpen={activeModal === 'privateSale'}
        onClose={() => setActiveModal(null)}
        projectAddress={projectAddress}
        projectName={projectName}
        privateSaleTime={7 * 24 * 60 * 60} // 7 days in seconds
        onSuccess={() => {
          console.log('Private sale started')
          setActiveModal(null)
        }}
      />
      
      {/* Start Public Sale Modal */}
      <StartPublicSaleModal
        isOpen={activeModal === 'publicSale'}
        onClose={() => setActiveModal(null)}
        projectAddress={projectAddress}
        projectName={projectName}
        publicSaleTime={14 * 24 * 60 * 60} // 14 days in seconds
        onSuccess={() => {
          console.log('Public sale started')
          setActiveModal(null)
        }}
      />
      
      {/* End Sales Modal */}
      <EndSalesModal
        isOpen={activeModal === 'endSales'}
        onClose={() => setActiveModal(null)}
        projectAddress={projectAddress}
        projectName={projectName}
        onSuccess={() => {
          console.log('Sales ended')
          setActiveModal(null)
        }}
      />
      
      {/* Public End Sales Modal */}
      <PublicEndSalesModal
        isOpen={activeModal === 'publicEnd'}
        onClose={() => setActiveModal(null)}
        projectAddress={projectAddress}
        projectName={projectName}
        canPublicEnd={true} // Would need logic to determine this
        onSuccess={() => {
          console.log('Sales ended via emergency mechanism')
          setActiveModal(null)
        }}
      />
 
      {/* Floating Signature Helper */}
      <FloatingSignatureHelper />
    </div>
  )
}

Client Investment Interface

import { 
  PurchaseModal,
  RefundModal,
  ClaimModal,
  ClaimNFTModal,
  DepositModal
} from '@/components/ifif-project-client-modals'
import { useProjectDetailData } from '@/lib/progressive-ifif-hooks'
import { useUserProfileData } from '@/lib/progressive-user-hooks'
import { useCachedIFIFData } from '@/lib/progressive-ifif-hooks'
import { useAccount } from 'wagmi'
 
function ClientInvestmentInterface({ 
  projectId,
  projectAddress
}: { 
  projectId: number
  projectAddress?: Address
}) {
  const [modals, setModals] = useState({
    purchase: false,
    refund: false,
    claim: false,
    claimNFT: false,
    deposit: false
  })
 
  // Get wallet connection status
  const { isConnected: walletConnected, address } = useAccount()
  
  // Get project details and overview data
  const { project, overview, isLoading } = useProjectDetailData(projectId)
  
  // Get user profile data for investment tracking
  const { profile: userProfile, investments: userInvestments } = useUserProfileData(address || '')
  
  // Get cached IFIF data for purchase/refund/claim tracking
  const { purchases, refunds, claims } = useCachedIFIFData()
 
  // Calculate user-specific investment data for this project
  const userData = useMemo(() => {
    if (!address || !overview?.projectAddress) {
      return {
        hasInvestment: false,
        canRefund: false,
        canClaim: false,
        canClaimNFT: false,
        netInvestment: 0,
        userInvestment: null
      }
    }
 
    // Find user investment for this specific project
    const projectInvestment = userInvestments.find(
      inv => inv.projectAddress.toLowerCase() === overview.projectAddress.toLowerCase()
    )
 
    if (!projectInvestment) {
      return {
        hasInvestment: false,
        canRefund: false,
        canClaim: false,
        canClaimNFT: false,
        netInvestment: 0,
        userInvestment: null
      }
    }
 
    // Calculate net investment (total invested - refunded)
    const totalInvested = Number(formatEther(BigInt(projectInvestment.totalInvested)))
    const totalRefunded = Number(formatEther(BigInt(projectInvestment.totalRefunded)))
    const netInvestment = totalInvested - totalRefunded
 
    // Determine what actions are available
    const hasInvestment = netInvestment > 0
    const canRefund = hasInvestment && overview.stage === 5 // SALE_FAILED
    const canClaim = hasInvestment && overview.stage === 6 && !projectInvestment.tokenAllocation // CLAIM stage
    const canClaimNFT = hasInvestment && overview.stage === 4 // SALE_SUCCESSED
 
    return {
      hasInvestment,
      canRefund,
      canClaim,
      canClaimNFT,
      netInvestment,
      userInvestment: projectInvestment
    }
  }, [address, overview, userInvestments])
 
  const openModal = (modalName: keyof typeof modals) => {
    setModals(prev => ({ ...prev, [modalName]: true }))
  }
 
  const closeModal = (modalName: keyof typeof modals) => {
    setModals(prev => ({ ...prev, [modalName]: false }))
  }
 
  if (isLoading) {
    return (
      <div className="bg-white border border-slate-200 space-y-4">
        <div className="p-6 pb-0">
          <h2 className="text-lg font-semibold">Client Actions</h2>
        </div>
        <div className="px-6 pb-6 space-y-3">
          {[...Array(5)].map((_, i) => (
            <div key={i} className="h-12 bg-slate-200 rounded animate-pulse" />
          ))}
        </div>
      </div>
    )
  }
 
  return (
    <div className="bg-white border border-slate-200 space-y-4">
      <div className="p-6 pb-0">
        <div className="flex items-center justify-between">
          <h2 className="text-lg font-semibold flex items-center gap-2">
            <Wallet className="h-5 w-5" />
            Client Actions
          </h2>
          {walletConnected && (
            <Badge variant="default" className="bg-blue-100 text-blue-800 border-blue-200">
              Connected
            </Badge>
          )}
        </div>
        
        {/* User Investment Summary */}
        {userData.hasInvestment && (
          <div className="mt-4 p-3 bg-slate-50 rounded-lg">
            <div className="flex items-center justify-between">
              <span className="text-sm text-slate-600">Your Investment:</span>
              <span className="font-medium">
                {userData.netInvestment.toFixed(4)} ETH
              </span>
            </div>
          </div>
        )}
      </div>
      
      <div className="px-6 pb-6 space-y-3">
        {/* Purchase Action */}
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!walletConnected || !overview || (overview.stage !== 2 && overview.stage !== 3)}
          onClick={() => openModal('purchase')}
        >
          <ShoppingCart className="h-4 w-4 mr-2" />
          Purchase Tokens
          {overview?.stage === 2 && (
            <Badge variant="outline" className="ml-auto bg-purple-50 text-purple-700 border-purple-200">
              Private Sale
            </Badge>
          )}
          {overview?.stage === 3 && (
            <Badge variant="outline" className="ml-auto bg-blue-50 text-blue-700 border-blue-200">
              Public Sale
            </Badge>
          )}
        </Button>
        
        {/* Refund Action */}
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!walletConnected || !userData.canRefund}
          onClick={() => openModal('refund')}
        >
          <RefreshCw className="h-4 w-4 mr-2" />
          Request Refund
          {userData.canRefund && (
            <Badge variant="outline" className="ml-auto bg-red-50 text-red-700 border-red-200">
              Available
            </Badge>
          )}
        </Button>
        
        {/* Claim NFT Action */}
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!walletConnected || !userData.canClaimNFT}
          onClick={() => openModal('claimNFT')}
        >
          <Coins className="h-4 w-4 mr-2" />
          Claim Investment NFT
          {userData.canClaimNFT && (
            <Badge variant="outline" className="ml-auto bg-green-50 text-green-700 border-green-200">
              Available
            </Badge>
          )}
        </Button>
        
        {/* Direct Claim Action */}
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!walletConnected || !userData.canClaim}
          onClick={() => openModal('claim')}
        >
          <Gift className="h-4 w-4 mr-2" />
          Claim Tokens (Direct)
          {userData.canClaim && (
            <Badge variant="outline" className="ml-auto bg-green-50 text-green-700 border-green-200">
              Available
            </Badge>
          )}
        </Button>
        
        {/* Deposit Action for Distributors */}
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!walletConnected || !overview || overview.stage !== 4}
          onClick={() => openModal('deposit')}
        >
          <TrendingDown className="h-4 w-4 mr-2" />
          Deposit Tokens (Distributor)
        </Button>
      </div>
 
      {/* Purchase Modal */}
      <PurchaseModal
        isOpen={modals.purchase}
        onClose={() => closeModal('purchase')}
        projectAddress={overview?.projectAddress}
        projectId={projectId}
        fundTokenAddress="0x..." // Would be provided from project config
        merkleProof={[]} // Would be calculated from whitelist
        isVIP={false} // Would be determined from user status
        onSuccess={(txHash) => {
          console.log('Purchase successful:', txHash)
          closeModal('purchase')
        }}
        onApprovalSuccess={(txHash) => {
          console.log('Token approval successful:', txHash)
        }}
      />
      
      {/* Refund Modal */}
      <RefundModal
        isOpen={modals.refund}
        onClose={() => closeModal('refund')}
        projectAddress={overview?.projectAddress}
        projectId={projectId}
        fundTokenAddress="0x..." // Would be provided from project config
        onSuccess={(txHash) => {
          console.log('Refund processed:', txHash)
          closeModal('refund')
        }}
      />
      
      {/* Claim Modal */}
      <ClaimModal
        isOpen={modals.claim}
        onClose={() => closeModal('claim')}
        projectAddress={overview?.projectAddress}
        fundTokenAddress="0x..." // Would be provided from project config
        onSuccess={(txHash) => {
          console.log('Tokens claimed:', txHash)
          closeModal('claim')
        }}
      />
      
      {/* Claim NFT Modal */}
      <ClaimNFTModal
        isOpen={modals.claimNFT}
        onClose={() => closeModal('claimNFT')}
        projectAddress={overview?.projectAddress}
        onSuccess={(txHash) => {
          console.log('NFT claimed:', txHash)
          closeModal('claimNFT')
        }}
      />
      
      {/* Deposit Modal */}
      <DepositModal
        isOpen={modals.deposit}
        onClose={() => closeModal('deposit')}
        projectAddress={overview?.projectAddress}
        fundTokenAddress="0x..." // Would be provided from project config
        onSuccess={(txHash) => {
          console.log('Deposit successful:', txHash)
          closeModal('deposit')
        }}
        onApprovalSuccess={(txHash) => {
          console.log('Token approval successful:', txHash)
        }}
      />
    </div>
  )
}

NFT Operations Interface

import { 
  SplitNFTModal,
  MergeNFTModal,
  ConvertNFTModal
} from '@/components/ifif-nft-action-modals'
import { useProjectNFTData, useProjectDetailData } from '@/lib/progressive-ifif-hooks'
import { useAccount } from 'wagmi'
import { useSplitNFT, useMergeNFT, useConvertNFT } from '@/lib/ifif-nft-management-hooks'
 
function NFTOperationsInterface({ 
  projectId,
  selectedNFT,
  userAddress
}: { 
  projectId: string
  selectedNFT?: any // NFT allocation data
  userAddress?: Address
}) {
  const [modals, setModals] = useState({
    split: false,
    merge: false,
    convert: false
  })
 
  // Get wallet connection status
  const { address: connectedWallet } = useAccount()
  
  // Get project details for stage checking
  const { project } = useProjectDetailData(parseInt(projectId) || 0)
  
  // Get project NFT data for available NFTs
  const { allocations, operations, isLoading } = useProjectNFTData(project?.id || '')
 
  // Get NFT operation hooks for transaction management
  const { splitNFT, isLoading: isSplitting } = useSplitNFT()
  const { mergeNFT, isLoading: isMerging } = useMergeNFT()
  const { convertNFT, isLoading: isConverting } = useConvertNFT()
 
  // Calculate operation availability based on NFT and project state
  const operationStates = useMemo(() => {
    if (!selectedNFT || !connectedWallet || !project?.overview) {
      return {
        canSplit: false,
        canMerge: false,
        canConvert: false,
        isOwner: false
      }
    }
 
    const isOwner = connectedWallet.toLowerCase() === selectedNFT.owner?.toLowerCase()
    const isStageValid = project.overview.stage === 4 // SALE_SUCCESSED
    const isNFTActive = selectedNFT.isActive
 
    // Get user's NFTs for merge operations
    const userNFTs = allocations.filter(nft => 
      nft.owner?.toLowerCase() === connectedWallet.toLowerCase() && 
      nft.isActive &&
      nft.id !== selectedNFT.id
    )
 
    return {
      canSplit: isOwner && isNFTActive && isStageValid,
      canMerge: isOwner && isStageValid && userNFTs.length > 0,
      canConvert: isOwner && isNFTActive && isStageValid,
      isOwner,
      availableForMerge: userNFTs
    }
  }, [selectedNFT, connectedWallet, project?.overview, allocations])
 
  const openModal = (modalName: keyof typeof modals) => {
    setModals(prev => ({ ...prev, [modalName]: true }))
  }
 
  const closeModal = (modalName: keyof typeof modals) => {
    setModals(prev => ({ ...prev, [modalName]: false }))
  }
 
  if (!selectedNFT) {
    return (
      <div className="bg-white border border-slate-200 p-6 space-y-6">
        <div>
          <h3 className="text-lg font-semibold text-slate-900 mb-2">NFT Operations</h3>
          <p className="text-sm text-slate-600">
            Select an NFT to perform operations
          </p>
        </div>
      </div>
    )
  }
 
  return (
    <div className="bg-white border border-slate-200 space-y-4">
      <div className="p-6 pb-0">
        <h3 className="text-lg font-semibold text-slate-900 mb-2">NFT Operations</h3>
        <div className="space-y-1">
          <p className="text-sm text-slate-600">
            Token #{selectedNFT.tokenId}
          </p>
          <p className="text-sm text-slate-600">
            Weight: {formatEther(selectedNFT.weight || 0)} units
          </p>
          <div className="flex items-center gap-2">
            <Badge variant={selectedNFT.isActive ? "default" : "outline"}>
              {selectedNFT.isActive ? "Active" : "Inactive"}
            </Badge>
            {operationStates.isOwner && (
              <Badge variant="outline" className="bg-blue-50 text-blue-700 border-blue-200">
                Owner
              </Badge>
            )}
          </div>
        </div>
      </div>
 
      <div className="px-6 pb-6 space-y-3">
        {/* Split NFT Action */}
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!operationStates.canSplit || isSplitting}
          onClick={() => openModal('split')}
        >
          <Scissors className="h-4 w-4 mr-2" />
          Split NFT
          {operationStates.canSplit && (
            <Badge variant="outline" className="ml-auto bg-green-50 text-green-700 border-green-200">
              Available
            </Badge>
          )}
        </Button>
        
        {/* Merge NFT Action */}
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!operationStates.canMerge || isMerging}
          onClick={() => openModal('merge')}
        >
          <MergeIcon className="h-4 w-4 mr-2" />
          Merge NFTs
          {operationStates.canMerge && (
            <Badge variant="outline" className="ml-auto bg-green-50 text-green-700 border-green-200">
              {operationStates.availableForMerge?.length} available
            </Badge>
          )}
        </Button>
        
        {/* Convert NFT Action */}
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!operationStates.canConvert || isConverting}
          onClick={() => openModal('convert')}
        >
          <ArrowRightLeft className="h-4 w-4 mr-2" />
          Convert to Tokens
          {operationStates.canConvert && (
            <Badge variant="outline" className="ml-auto bg-green-50 text-green-700 border-green-200">
              Available
            </Badge>
          )}
        </Button>
 
        {/* Operation Status Info */}
        {!operationStates.isOwner && connectedWallet && (
          <Alert className="mt-4">
            <Info className="h-4 w-4" />
            <AlertDescription>
              You can only perform operations on NFTs you own.
            </AlertDescription>
          </Alert>
        )}
        
        {!connectedWallet && (
          <Alert className="mt-4">
            <Wallet className="h-4 w-4" />
            <AlertDescription>
              Connect your wallet to perform NFT operations.
            </AlertDescription>
          </Alert>
        )}
      </div>
 
      {/* Split NFT Modal */}
      <SplitNFTModal
        isOpen={modals.split}
        onClose={() => closeModal('split')}
        nftId={selectedNFT.id}
        projectAddress={project?.projectAddress}
        currentWeight={selectedNFT.weight}
        currentStage={project?.overview?.stage}
        nftOwner={selectedNFT.owner}
        onSuccess={() => {
          console.log('NFT split successful')
          closeModal('split')
        }}
      />
      
      {/* Merge NFT Modal */}
      <MergeNFTModal
        isOpen={modals.merge}
        onClose={() => closeModal('merge')}
        availableNFTs={operationStates.availableForMerge?.map(nft => ({
          id: nft.id,
          tokenId: nft.tokenId,
          weight: nft.weight,
          isActive: nft.isActive
        })) || []}
        projectAddress={project?.projectAddress}
        currentStage={project?.overview?.stage}
        nftOwner={selectedNFT.owner}
        onSuccess={() => {
          console.log('NFTs merged successfully')
          closeModal('merge')
        }}
      />
      
      {/* Convert NFT Modal */}
      <ConvertNFTModal
        isOpen={modals.convert}
        onClose={() => closeModal('convert')}
        nftId={selectedNFT.id}
        projectAddress={project?.projectAddress}
        nftWeight={selectedNFT.weight}
        currentStage={project?.overview?.stage}
        nftOwner={selectedNFT.owner}
        onSuccess={() => {
          console.log('NFT converted successfully')
          closeModal('convert')
        }}
      />
    </div>
  )
}
 
// Example usage in project NFT page context
function ProjectNFTPageIntegration({ projectId }: { projectId: string }) {
  const [selectedNFT, setSelectedNFT] = useState<any>(null)
  
  // Get project NFT data
  const { allocations, operations, isLoading } = useProjectNFTData(projectId)
  
  // NFT selection handler for table row clicks
  const handleNFTSelection = (nft: any) => {
    setSelectedNFT(nft)
  }
 
  return (
    <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
      {/* NFT List/Table */}
      <div className="lg:col-span-2">
        {/* NFT allocation table would go here */}
        <div className="bg-white border border-slate-200">
          {/* Table implementation with row selection */}
          {allocations.map(nft => (
            <div 
              key={nft.id} 
              onClick={() => handleNFTSelection(nft)}
              className="p-4 border-b cursor-pointer hover:bg-slate-50"
            >
              NFT #{nft.tokenId} - Weight: {formatEther(nft.weight)}
            </div>
          ))}
        </div>
      </div>
      
      {/* NFT Operations Panel */}
      <div>
        <NFTOperationsInterface
          projectId={projectId}
          selectedNFT={selectedNFT}
        />
      </div>
    </div>
  )
}

Error Handling

Comprehensive Error Management

import { useState, useCallback } from 'react'
import { useWriteContract } from 'wagmi'
import { Address, parseUnits } from 'viem'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { AlertTriangle } from 'lucide-react'
import { TransactionInfo } from '@/components/ui/transaction-info'
import { useSettingsStore } from '@/lib/settings-store'
import { useTransactionQueue } from '@/lib/transaction-queue-store'
 
// Real error state interface used across IFIF hooks
interface ProjectOperationState {
  isLoading: boolean
  isSuccess: boolean
  isError: boolean
  error: string | null
  txHash: string | null
}
 
function IFIFProjectManagement({ projectAddress }: { projectAddress: Address }) {
  const [state, setState] = useState<ProjectOperationState>({
    isLoading: false,
    isSuccess: false,
    isError: false,
    error: null,
    txHash: null
  })
 
  const { writeContractAsync } = useWriteContract()
  const { config } = useSettingsStore()
  const { addTransaction, updateTransaction } = useTransactionQueue()
 
  const handlePurchase = useCallback(async (amount: string) => {
    let transactionId: string | undefined
    
    try {
      setState({
        isLoading: true,
        isSuccess: false,
        isError: false,
        error: null,
        txHash: null
      })
 
      // Validate contract configuration (real pattern)
      if (!config?.contracts?.["IFIF"]) {
        throw new Error('IFIF contract not configured. Please check your connection settings.')
      }
 
      const ififContract = config.contracts["IFIF"]
      if (!ififContract.abi) {
        throw new Error('IFIF contract ABI not available.')
      }
 
      // Execute transaction with error handling
      const hash = await writeContractAsync({
        address: projectAddress,
        abi: ififContract.abi,
        functionName: 'purchase',
        args: [parseUnits(amount, 18), '0x123...', []]
      })
 
      // Add to transaction queue (real pattern)
      transactionId = addTransaction({
        hash,
        type: 'Purchase Investment',
        description: `Purchase ${amount} tokens in project`,
        status: 'pending'
      })
 
      setState({
        isLoading: false,
        isSuccess: true,
        isError: false,
        error: null,
        txHash: hash
      })
 
      return hash
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Purchase failed'
      
      // Update transaction with error if it was added
      if (transactionId) {
        updateTransaction(transactionId, { 
          status: 'failed', 
          error: errorMessage 
        })
      }
      
      setState({
        isLoading: false,
        isSuccess: false,
        isError: true,
        error: errorMessage,
        txHash: null
      })
      throw error
    }
  }, [writeContractAsync, config, addTransaction, updateTransaction, projectAddress])
 
  const reset = useCallback(() => {
    setState({
      isLoading: false,
      isSuccess: false,
      isError: false,
      error: null,
      txHash: null
    })
  }, [])
 
  return (
    <div className="space-y-6">
      {/* Contract Configuration Error */}
      {!config?.contracts?.["IFIF"] && (
        <Alert className="border-yellow-200 bg-yellow-50">
          <AlertTriangle className="h-4 w-4" />
          <AlertDescription>
            <strong>Configuration Required:</strong> IFIF contract not configured. 
            Please check your connection settings.
          </AlertDescription>
        </Alert>
      )}
 
      {/* Real TransactionInfo component for comprehensive status display */}
      <TransactionInfo
        isLoading={state.isLoading}
        isSuccess={state.isSuccess}
        isError={state.isError}
        error={state.error}
        txHash={state.txHash}
        loadingText="Processing investment..."
        successText="Investment submitted successfully!"
      />
 
      {/* Operation buttons with real state management */}
      <div className="flex gap-3">
        <Button 
          onClick={() => handlePurchase('100')}
          disabled={state.isLoading || !config?.contracts?.["IFIF"]}
        >
          {state.isLoading ? 'Processing...' : 'Invest 100 USDC'}
        </Button>
        
        {(state.isError || state.isSuccess) && (
          <Button 
            variant="outline" 
            onClick={reset}
          >
            Reset
          </Button>
        )}
      </div>
    </div>
  )
}

Real Error Categorization and Recovery

import { useState, useCallback } from 'react'
import { usePurchase, useUpdateProjectConfig } from '@/lib/ifif-project-management-hooks'
import { useAccount } from 'wagmi'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { AlertTriangle } from 'lucide-react'
import { TransactionInfo } from '@/components/ui/transaction-info'
 
// Real error categorization based on actual hook patterns
function ProjectOperationManager({ projectAddress }: { projectAddress: Address }) {
  const { address: userAddress } = useAccount()
  
  // Real hook with comprehensive error handling
  const {
    purchase,
    isLoading: isPurchasing,
    isSuccess: purchaseSuccess,
    isError: purchaseError,
    error: purchaseErrorMessage,
    txHash: purchaseTxHash,
    reset: resetPurchase
  } = usePurchase()
 
  const {
    updateProjectConfig,
    isLoading: isUpdating,
    isSuccess: updateSuccess,
    isError: updateError,
    error: updateErrorMessage,
    txHash: updateTxHash,
    reset: resetUpdate
  } = useUpdateProjectConfig()
 
  // Real error categorization patterns from actual hooks
  const [approvalError, setApprovalError] = useState<string | null>(null)
  const [configError, setConfigError] = useState<string | null>(null)
  const [walletError, setWalletError] = useState<string | null>(null)
 
  const handlePurchase = useCallback(async (amount: bigint) => {
    try {
      // Clear previous errors
      setApprovalError(null)
      setConfigError(null)
      setWalletError(null)
      
      await purchase({
        projectAddress,
        amount,
        fundTokenAddress: '0x123...' as Address,
        merkleProof: []
      })
    } catch (error) {
      // Real error categorization pattern from actual hooks
      const errorMessage = error instanceof Error ? error.message : 'Transaction failed'
      
      // Contract configuration errors (from actual hooks)
      if (errorMessage.includes('contract not configured') || 
          errorMessage.includes('connection settings')) {
        setConfigError(errorMessage)
      }
      // Wallet connection errors (from actual hooks)
      else if (errorMessage.includes('connect your wallet') ||
               errorMessage.includes('Please connect your wallet')) {
        setWalletError(errorMessage)
      }
      // Approval/allowance errors (from NFT modals pattern)
      else if (errorMessage.toLowerCase().includes('approve') || 
               errorMessage.toLowerCase().includes('allowance')) {
        setApprovalError(errorMessage)
      } else {
        console.error('Failed to purchase tokens:', error)
      }
    }
  }, [purchase, projectAddress])
 
  const handleConfigUpdate = useCallback(async (nextConfig: any, signatures: [string, string, string]) => {
    try {
      setApprovalError(null)
      setConfigError(null)
      setWalletError(null)
      
      await updateProjectConfig({
        projectAddress,
        nextConfig,
        signatures
      })
    } catch (error) {
      // Same error categorization patterns
      const errorMessage = error instanceof Error ? error.message : 'Configuration update failed'
      
      if (errorMessage.includes('contract not configured')) {
        setConfigError(errorMessage)
      } else if (errorMessage.includes('connect your wallet')) {
        setWalletError(errorMessage)
      } else {
        console.error('Failed to update project config:', error)
      }
    }
  }, [updateProjectConfig, projectAddress])
 
  return (
    <div className="space-y-6">
      {/* Contract Configuration Errors */}
      {configError && (
        <Alert className="border-yellow-200 bg-yellow-50">
          <AlertTriangle className="h-4 w-4" />
          <AlertDescription>
            <strong>Configuration Error:</strong> {configError}
            <div className="mt-2">
              <Button size="sm" variant="outline" onClick={() => setConfigError(null)}>
                Dismiss
              </Button>
            </div>
          </AlertDescription>
        </Alert>
      )}
 
      {/* Wallet Connection Errors */}
      {walletError && (
        <Alert className="border-orange-200 bg-orange-50">
          <AlertTriangle className="h-4 w-4" />
          <AlertDescription>
            <strong>Wallet Required:</strong> {walletError}
            <div className="mt-2">
              <Button size="sm" variant="outline" onClick={() => setWalletError(null)}>
                Dismiss
              </Button>
            </div>
          </AlertDescription>
        </Alert>
      )}
 
      {/* Approval-specific Error Handling */}
      {approvalError && (
        <Alert className="border-red-200 bg-red-50">
          <AlertTriangle className="h-4 w-4" />
          <AlertDescription>
            <strong>Approval Failed:</strong> {approvalError}
            <div className="mt-2">
              <Button size="sm" variant="outline" onClick={() => setApprovalError(null)}>
                Retry Approval
              </Button>
            </div>
          </AlertDescription>
        </Alert>
      )}
 
      {/* Purchase Transaction Status */}
      <div className="space-y-2">
        <h4 className="font-medium">Purchase Tokens</h4>
        <TransactionInfo
          isLoading={isPurchasing}
          isSuccess={purchaseSuccess}
          isError={purchaseError && !approvalError && !configError && !walletError}
          error={purchaseErrorMessage}
          txHash={purchaseTxHash}
          loadingText="Processing token purchase..."
          successText="Tokens purchased successfully!"
        />
        <div className="flex gap-2">
          <Button 
            onClick={() => handlePurchase(parseUnits('100', 18))}
            disabled={isPurchasing || !userAddress}
          >
            Purchase 100 Tokens
          </Button>
          {(purchaseError || purchaseSuccess) && (
            <Button variant="outline" onClick={resetPurchase}>
              Reset
            </Button>
          )}
        </div>
      </div>
 
      {/* Configuration Update Status */}
      <div className="space-y-2">
        <h4 className="font-medium">Update Configuration</h4>
        <TransactionInfo
          isLoading={isUpdating}
          isSuccess={updateSuccess}
          isError={updateError && !configError && !walletError}
          error={updateErrorMessage}
          txHash={updateTxHash}
          loadingText="Updating project configuration..."
          successText="Configuration updated successfully!"
        />
        <div className="flex gap-2">
          <Button
            onClick={() => handleConfigUpdate(
              {
                projectOwner: '0x...' as Address,
                distributor: '0x...' as Address,
                distributorFeePercent: 500,
                projectOwnerFeePercent: 300,
                platformFeePercent: 200,
                pairPrice: parseUnits('1', 18)
              },
              ['0x...', '0x...', '0x...']
            )}
            disabled={isUpdating || !userAddress}
          >
            Update Configuration
          </Button>
          {(updateError || updateSuccess) && (
            <Button variant="outline" onClick={resetUpdate}>
              Reset
            </Button>
          )}
        </div>
      </div>
    </div>
  )
}

Stage-Based Error Prevention

import { useProjectDetailData } from '@/lib/progressive-ifif-hooks'
import { useAccount } from 'wagmi'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { AlertTriangle, Info } from 'lucide-react'
import { PROJECT_STAGES } from '@/config/constants'
 
// Real stage validation patterns from actual project pages
function StageAwareOperations({ projectId }: { projectId: string }) {
  const { address: walletConnected } = useAccount()
  const { project, overview, isLoading } = useProjectDetailData(parseInt(projectId) || 0)
 
  // Real stage validation logic from project pages
  const stageValidations = useMemo(() => {
    if (!overview) return null
 
    const currentStage = overview.stage
    const stageInfo = PROJECT_STAGES[currentStage] || { 
      name: 'Unknown', 
      color: 'bg-gray-500', 
      description: 'Unknown stage' 
    }
 
    return {
      // From actual project/[id]/page.tsx validation patterns
      canPurchase: currentStage === 2 || currentStage === 3, // PRIVATE_SALE or PUBLIC_SALE
      canRefund: currentStage === 5, // SALE_FAILED
      canClaimNFT: currentStage === 4, // SALE_SUCCESSED  
      canClaimTokens: currentStage === 6, // CLAIM
      canDeposit: currentStage === 4, // SALE_SUCCESSED (for distributors)
      
      // From NFT action modals validation patterns
      canSplitNFT: currentStage === 4, // SALE_SUCCESSED
      canMergeNFT: currentStage === 4, // SALE_SUCCESSED
      canConvertNFT: currentStage === 4, // SALE_SUCCESSED
      
      // Management operations (project owner only)
      canUpdateConfig: currentStage === 1, // INITIALIZED
      canStartPrivate: currentStage === 1, // INITIALIZED
      canStartPublic: currentStage === 2, // PRIVATE_SALE
      canEndSales: currentStage === 2 || currentStage === 3, // PRIVATE_SALE or PUBLIC_SALE
      canPublicEnd: currentStage === 3, // PUBLIC_SALE
      
      currentStage,
      stageInfo
    }
  }, [overview])
 
  if (isLoading) {
    return <div>Loading project stage validation...</div>
  }
 
  if (!stageValidations) {
    return (
      <Alert className="border-red-200 bg-red-50">
        <AlertTriangle className="h-4 w-4" />
        <AlertDescription>
          <strong>Project Not Found:</strong> Unable to load project stage information.
        </AlertDescription>
      </Alert>
    )
  }
 
  const { 
    canPurchase, canRefund, canClaimNFT, canClaimTokens, canDeposit,
    canSplitNFT, canMergeNFT, canConvertNFT,
    canUpdateConfig, canStartPrivate, canStartPublic, canEndSales, canPublicEnd,
    currentStage, stageInfo 
  } = stageValidations
 
  return (
    <div className="space-y-6">
      {/* Current Stage Information */}
      <div className="bg-slate-50 p-4 rounded-lg">
        <h4 className="font-medium text-slate-900 mb-2">Current Project Stage</h4>
        <div className="flex items-center gap-2">
          <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${stageInfo.color.replace('bg-', 'bg-').replace('-500', '-100')} text-${stageInfo.color.split('-')[1]}-800`}>
            Stage {currentStage}: {stageInfo.name}
          </span>
          <span className="text-sm text-slate-600">{stageInfo.description}</span>
        </div>
      </div>
 
      {/* Client Investment Operations */}
      <div className="space-y-4">
        <h4 className="font-medium text-slate-900">Client Investment Operations</h4>
        
        {/* Purchase validation */}
        {!canPurchase && (
          <Alert className="border-orange-200 bg-orange-50">
            <AlertTriangle className="h-4 w-4" />
            <AlertDescription>
              <strong>Purchase Not Available:</strong> Token purchase is only available during 
              Private Sale (stage 2) or Public Sale (stage 3). 
              Current stage: {currentStage} ({stageInfo.name})
            </AlertDescription>
          </Alert>
        )}
 
        {/* Refund validation */}
        {!canRefund && currentStage > 3 && (
          <Alert className="border-orange-200 bg-orange-50">
            <AlertTriangle className="h-4 w-4" />
            <AlertDescription>
              <strong>Refund Not Available:</strong> Refunds are only available when 
              the sale has failed (stage 5). Current stage: {currentStage} ({stageInfo.name})
            </AlertDescription>
          </Alert>
        )}
 
        {/* NFT Claim validation */}
        {!canClaimNFT && currentStage > 2 && (
          <Alert className="border-orange-200 bg-orange-50">
            <AlertTriangle className="h-4 w-4" />
            <AlertDescription>
              <strong>NFT Claim Not Available:</strong> NFT claiming is only available after 
              successful sale completion (stage 4). Current stage: {currentStage} ({stageInfo.name})
            </AlertDescription>
          </Alert>
        )}
 
        {/* Token Claim validation */}
        {!canClaimTokens && currentStage !== 6 && (
          <Alert className="border-orange-200 bg-orange-50">
            <AlertTriangle className="h-4 w-4" />
            <AlertDescription>
              <strong>Token Claim Not Available:</strong> Direct token claiming is only available 
              during the claim phase (stage 6). Current stage: {currentStage} ({stageInfo.name})
            </AlertDescription>
          </Alert>
        )}
 
        {/* Operation buttons with real stage validation */}
        <div className="flex flex-wrap gap-2">
          <Button 
            disabled={!canPurchase || !walletConnected}
            variant={canPurchase ? "default" : "outline"}
          >
            Purchase Tokens
          </Button>
          <Button 
            disabled={!canRefund || !walletConnected}
            variant={canRefund ? "default" : "outline"}
          >
            Request Refund
          </Button>
          <Button 
            disabled={!canClaimNFT || !walletConnected}
            variant={canClaimNFT ? "default" : "outline"}
          >
            Claim NFT
          </Button>
          <Button 
            disabled={!canClaimTokens || !walletConnected}
            variant={canClaimTokens ? "default" : "outline"}
          >
            Claim Tokens
          </Button>
        </div>
      </div>
 
      {/* NFT Operations */}
      <div className="space-y-4">
        <h4 className="font-medium text-slate-900">NFT Operations</h4>
        
        {/* NFT operations validation (from actual NFT modals) */}
        {!canSplitNFT && (
          <Alert className="border-orange-200 bg-orange-50">
            <AlertTriangle className="h-4 w-4" />
            <AlertDescription>
              <strong>NFT Split Not Available:</strong> NFT splitting is only available during 
              SALE_SUCCESSED stage (stage 4). Current stage: {currentStage} ({stageInfo.name})
            </AlertDescription>
          </Alert>
        )}
 
        {!canMergeNFT && (
          <Alert className="border-orange-200 bg-orange-50">
            <AlertTriangle className="h-4 w-4" />
            <AlertDescription>
              <strong>NFT Merge Not Available:</strong> NFT merging is only available during 
              SALE_SUCCESSED stage (stage 4). Current stage: {currentStage} ({stageInfo.name})
            </AlertDescription>
          </Alert>
        )}
 
        {!canConvertNFT && (
          <Alert className="border-orange-200 bg-orange-50">
            <AlertTriangle className="h-4 w-4" />
            <AlertDescription>
              <strong>NFT Convert Not Available:</strong> NFT conversion is only available during 
              SALE_SUCCESSED stage (stage 4). Current stage: {currentStage} ({stageInfo.name})
            </AlertDescription>
          </Alert>
        )}
 
        <div className="flex flex-wrap gap-2">
          <Button 
            disabled={!canSplitNFT || !walletConnected}
            variant={canSplitNFT ? "default" : "outline"}
          >
            Split NFT
          </Button>
          <Button 
            disabled={!canMergeNFT || !walletConnected}
            variant={canMergeNFT ? "default" : "outline"}
          >
            Merge NFTs
          </Button>
          <Button 
            disabled={!canConvertNFT || !walletConnected}
            variant={canConvertNFT ? "default" : "outline"}
          >
            Convert to Tokens
          </Button>
        </div>
      </div>
 
      {/* Management Operations (Project Owner Only) */}
      <div className="space-y-4">
        <h4 className="font-medium text-slate-900">Management Operations</h4>
        
        <Alert className="border-blue-200 bg-blue-50">
          <Info className="h-4 w-4" />
          <AlertDescription>
            <strong>Management Access:</strong> These operations are only available to the project owner 
            and are subject to stage restrictions based on the project lifecycle.
          </AlertDescription>
        </Alert>
 
        <div className="flex flex-wrap gap-2">
          <Button 
            disabled={!canUpdateConfig}
            variant={canUpdateConfig ? "default" : "outline"}
            size="sm"
          >
            Update Config {!canUpdateConfig && '(Stage 1 only)'}
          </Button>
          <Button 
            disabled={!canStartPrivate}
            variant={canStartPrivate ? "default" : "outline"}
            size="sm"
          >
            Start Private Sale {!canStartPrivate && '(Stage 1 only)'}
          </Button>
          <Button 
            disabled={!canStartPublic}
            variant={canStartPublic ? "default" : "outline"}
            size="sm"
          >
            Start Public Sale {!canStartPublic && '(Stage 2 only)'}
          </Button>
          <Button 
            disabled={!canEndSales}
            variant={canEndSales ? "default" : "outline"}
            size="sm"
          >
            End Sales {!canEndSales && '(Stage 2-3 only)'}
          </Button>
        </div>
      </div>
 
      {/* Stage Progression Information */}
      <div className="bg-blue-50 p-4 rounded-lg">
        <h4 className="font-medium text-blue-900 mb-2">Stage Progression Guide</h4>
        <div className="text-sm text-blue-800 space-y-1">
          <p><strong>Stage 0-1:</strong> Project setup and configuration</p>
          <p><strong>Stage 2:</strong> Private Sale - Limited participant purchase phase</p>
          <p><strong>Stage 3:</strong> Public Sale - Open purchase phase</p>
          <p><strong>Stage 4:</strong> Sale Success - NFT operations and claiming available</p>
          <p><strong>Stage 5:</strong> Sale Failed - Refund phase</p>
          <p><strong>Stage 6:</strong> Claim Phase - Final token distribution</p>
        </div>
      </div>
    </div>
  )
}

Constants

IFIF Configuration

// Real configuration values from the IFIF system based on actual implementation
// examples/config/constants.ts
export const PROJECT_STAGES: Record<number, { name: string; color: string; description: string }> = {
  0: { 
    name: 'None', 
    color: 'bg-gray-500',
    description: 'Project not initialized'
  },
  1: { 
    name: 'Initialized', 
    color: 'bg-blue-500',
    description: 'Project initialized but not started'
  },
  2: { 
    name: 'Private Sale', 
    color: 'bg-yellow-500',
    description: 'Private sale is active'
  },
  3: { 
    name: 'Public Sale', 
    color: 'bg-orange-500',
    description: 'Public sale is active'
  },
  4: { 
    name: 'Sale Success', 
    color: 'bg-green-500',
    description: 'Sale completed successfully'
  },
  5: { 
    name: 'Sale Failed', 
    color: 'bg-red-500',
    description: 'Sale failed to reach target'
  },
  6: { 
    name: 'Claim', 
    color: 'bg-purple-500',
    description: 'Claim phase is active'
  }
}
 
export const ROLE_NAMES: Record<string, { name: string; color: string; description: string }> = {
  '0x0000000000000000000000000000000000000000000000000000000000000000': { 
    name: 'Default Admin', 
    color: 'bg-red-500',
    description: 'Super administrator with all permissions'
  },
  '0xa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775': { 
    name: 'Admin', 
    color: 'bg-blue-500',
    description: 'Administrative permissions'
  },
  '0x241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08': { 
    name: 'Manager', 
    color: 'bg-green-500',
    description: 'Management and operational permissions'
  },
  '0xfbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c': { 
    name: 'Distributor', 
    color: 'bg-purple-500',
    description: 'Distribution and allocation permissions'
  }
}
 
export const NFT_OPERATION_TYPES: Record<number, { name: string; color: string; description: string }> = {
  0: { 
    name: 'Claim', 
    color: 'bg-green-500',
    description: 'NFT claim operation'
  },
  1: { 
    name: 'Split', 
    color: 'bg-blue-500',
    description: 'NFT split operation'
  },
  2: { 
    name: 'Merge', 
    color: 'bg-purple-500',
    description: 'NFT merge operation'
  },
  3: { 
    name: 'Convert', 
    color: 'bg-orange-500',
    description: 'NFT conversion to tokens'
  }
}
 
// Progressive loading configuration from actual implementation
// examples/lib/progressive-ifif-hooks.ts
export const IFIF_PROGRESSIVE_CONFIG = {
  projects: { batchSize: 25, staleTime: 30000 },
  tokenDeployments: { batchSize: 30, staleTime: 60000 },
  tokenClaims: { batchSize: 100, staleTime: 30000 },
  nftOperations: { batchSize: 50, staleTime: 30000 },
  factoryDeployments: { batchSize: 20, staleTime: 60000 },
  userInvestments: { batchSize: 25, staleTime: 30000 },
  userPurchases: { batchSize: 50, staleTime: 30000 },
  userRefunds: { batchSize: 50, staleTime: 30000 },
  userClaims: { batchSize: 50, staleTime: 30000 },
  userNFTs: { batchSize: 30, staleTime: 30000 }
} as const
 
// Analytics configuration from actual implementation
// examples/config/constants.ts
export const ANALYTICS_PROGRESSIVE_CONFIG = {
  dailyMetrics: { batchSize: 30, staleTime: 120000 },
  summaryAnalytics: { batchSize: 10, staleTime: 300000 },
  trendsAnalysis: { batchSize: 50, staleTime: 180000 },
  performance: { batchSize: 25, staleTime: 240000 }
} as const
 
// Investment status types from actual implementation
// examples/lib/types.ts
type InvestmentStatus = 'active' | 'refunded' | 'claimed'
type TransactionType = 'purchase' | 'refund' | 'claim'

Security and Performance

Security Best Practices

The IFIF application implements comprehensive security patterns across all page types. Here are the actual security implementations from the codebase:

Connection and Authentication Patterns

// Real connection checking pattern used across all pages
// examples/app/ifif-projects/page.tsx, examples/app/user/[address]/page.tsx
import { useSettingsStore } from '@/lib/settings-store'
 
export default function SecurePage() {
  const { isConnected: _isConnected } = useSettingsStore()
  const [isClient, setIsClient] = useState(false)
  
  useEffect(() => {
    setIsClient(true)
  }, [])
 
  // Security: Prevent hydration mismatch and ensure client-side rendering
  const isConnected = useMemo(() => isClient ? _isConnected : false, [isClient, _isConnected])
 
  // Security: Block access to sensitive data without connection
  if (!isConnected) {
    return (
      <div className="flex-1 space-y-8 p-8">
        <div className="text-center py-20">
          <div className="bg-gradient-to-br from-blue-50 to-purple-50 p-8 max-w-md mx-auto">
            <h1 className="text-3xl font-bold text-slate-900 mb-3">Connection Required</h1>
            <p className="text-slate-600 mb-6 leading-relaxed">
              Connect to Ponder indexer to view data
            </p>
            <Badge variant="outline" className="text-yellow-700 border-yellow-300">
              Not Connected
            </Badge>
          </div>
        </div>
      </div>
    )
  }
 
  // Render secure content only after connection validation
  return <SecureContent />
}

Project Owner Access Control

// Real project ownership validation from examples/app/project/[id]/page.tsx
import { useAccount } from 'wagmi'
import { useProjectDetailData } from '@/lib/progressive-ifif-hooks'
import { useUserProfileData } from '@/lib/progressive-user-hooks'
 
export default function ProjectDetailPage() {
  const { isConnected: walletConnected, address } = useAccount()
  const { project, overview, isLoading, error } = useProjectDetailData(projectId)
  
  // Security: Verify project ownership for management actions
  const isProjectOwner = useMemo(() => {
    if (!walletConnected || !address || !overview?.owner) {
      return false
    }
    return address.toLowerCase() === overview.owner.toLowerCase()
  }, [walletConnected, address, overview?.owner])
 
  // Security: User investment calculation with address validation
  const { profile: userProfile, investments: userInvestments } = useUserProfileData(address || '')
  
  const userData = useMemo(() => {
    if (!address || !overview?.projectAddress) {
      return {
        userInvestment: 0,
        userTokens: 0,
        userPurchase: 0
      }
    }
 
    // Find user investment for this specific project using both sources
    const projectInvestment = userInvestments.find(
      inv => inv.projectAddress.toLowerCase() === overview.projectAddress.toLowerCase()
    )
 
    return calculateUserData(projectInvestment, address, overview)
  }, [address, overview?.projectAddress, userInvestments])
 
  return (
    <div>
      {/* Project Management Actions */}
      <div className="bg-white border border-slate-200 space-y-4">
        <div className="p-6 pb-0">
          <div className="flex items-center justify-between">
            <h2 className="text-lg font-semibold flex items-center gap-2">
              <Settings className="h-5 w-5" />
              Project Management
            </h2>
            {isProjectOwner && (
              <Badge variant="default" className="bg-green-100 text-green-800 border-green-200">
                Owner Access
              </Badge>
            )}
          </div>
        </div>
        
        {/* Management actions only for verified owners with stage validation */}
        <div className="px-6 pb-6 space-y-3">
          <Button 
            className="w-full justify-start" 
            variant="outline"
            disabled={!isProjectOwner || !overview || overview.stage !== 1}
            onClick={() => openModal('updateDetails')}
          >
            <Edit3 className="h-4 w-4 mr-2" />
            Update Project Details
          </Button>
          
          <Button 
            className="w-full justify-start" 
            variant="outline"
            disabled={!isProjectOwner || !overview || overview.stage === 6}
            onClick={() => openModal('updateConfig')}
          >
            <Settings className="h-4 w-4 mr-2" />
            Update Configuration
          </Button>
          
          <Button 
            className="w-full justify-start" 
            variant="outline"
            disabled={!isProjectOwner || !overview || overview.stage !== 1}
            onClick={() => openModal('startPrivateSale')}
          >
            <Users className="h-4 w-4 mr-2" />
            Start Private Sale
          </Button>
          
          <Button 
            className="w-full justify-start" 
            variant="outline"
            disabled={!isProjectOwner || !overview || overview.stage !== 2}
            onClick={() => openModal('startPublicSale')}
          >
            <Globe className="h-4 w-4 mr-2" />
            Start Public Sale
          </Button>
          
          <Button 
            className="w-full justify-start" 
            variant="outline"
            disabled={!isProjectOwner || !overview || (overview.stage !== 2 && overview.stage !== 3)}
            onClick={() => openModal('endSales')}
          >
            <Square className="h-4 w-4 mr-2" />
            End Sales
          </Button>
        </div>
      </div>
    </div>
  )
}

Address Validation and Input Security

// Real address validation patterns from components
import { isAddress } from 'viem'
import { useForm } from '@tanstack/react-form'
import { FieldInfo } from '@/components/ui/field-info'
 
function SecureAddressForm() {
  const form = useForm({
    defaultValues: {
      projectOwner: '' as Address,
      distributor: '' as Address,
      fundToken: '' as Address
    },
    onSubmit: async ({ value }) => {
      // All addresses validated before submission
      await processSecureForm(value)
    }
  })
 
  return (
    <div className="space-y-4">
      {/* Comprehensive validation pattern */}
      <form.Field
        name="projectOwner"
        validators={{
          onChange: ({ value }) =>
            !value || value.trim().length === 0
              ? 'Project owner address is required'
              : !isAddress(value as Address)
                ? 'Invalid address format'
                : undefined,
        }}
      >
        {(field) => (
          <div className="space-y-2">
            <Label htmlFor={field.name}>Project Owner Address</Label>
            <Input
              id={field.name}
              name={field.name}
              value={field.state.value}
              onBlur={field.handleBlur}
              onChange={(e) => field.handleChange(e.target.value)}
              placeholder="0x..."
              className={field.state.meta.isTouched && !field.state.meta.isValid ? 'border-red-300' : ''}
            />
            <FieldInfo field={field} />
          </div>
        )}
      </form.Field>
 
      {/* Simplified validation pattern for optional fields */}
      <form.Field
        name="distributor"
        validators={{
          onChange: ({ value }) => 
            value && !isAddress(value) ? 'Invalid address' : undefined,
        }}
      >
        {(field) => (
          <div className="space-y-2">
            <Label htmlFor={field.name}>Distributor Address (Optional)</Label>
            <Input
              id={field.name}
              name={field.name}
              value={field.state.value}
              onBlur={field.handleBlur}
              onChange={(e) => field.handleChange(e.target.value)}
              placeholder="0x..."
            />
            <FieldInfo field={field} />
          </div>
        )}
      </form.Field>
 
      {/* Advanced validation with sanitization */}
      <form.Field
        name="fundToken"
        validators={{
          onChange: ({ value }) => {
            if (!value || value.trim() === '') {
              return 'Fund token address is required'
            }
            if (!isAddress(value.trim() as Address)) {
              return 'Please enter a valid Ethereum address'
            }
            return undefined
          },
        }}
      >
        {(field) => (
          <div className="space-y-2">
            <Label htmlFor={field.name}>Fund Token Address</Label>
            <Input
              id={field.name}
              name={field.name}
              value={field.state.value}
              onBlur={field.handleBlur}
              onChange={(e) => field.handleChange(e.target.value)}
              placeholder="0x1234...abcd"
              required
              className={field.state.meta.errors.length > 0 ? 'border-red-500' : ''}
            />
            <FieldInfo field={field} />
            <p className="text-xs text-slate-500">
              Enter the token contract address for project funding
            </p>
          </div>
        )}
      </form.Field>
    </div>
  )
}

Multi-Layered Access Control

The IFIF application implements different security patterns depending on context:

// 1. Connection-Based Security (All Pages)
import { useAccount } from 'wagmi'
import { useSettingsStore } from '@/lib/settings-store'
 
export default function SecurePage() {
  const { isConnected: _isConnected } = useSettingsStore()
  const [isClient, setIsClient] = useState(false)
  
  useEffect(() => {
    setIsClient(true)
  }, [])
 
  const isConnected = useMemo(() => isClient ? _isConnected : false, [isClient, _isConnected])
 
  if (!isConnected) {
    return (
      <div className="flex-1 space-y-8 p-8">
        <div className="text-center py-20">
          <div className="bg-gradient-to-br from-blue-50 to-purple-50 p-8 max-w-md mx-auto">
            <FolderIcon className="mx-auto h-16 w-16 text-slate-400 mb-6" />
            <h1 className="text-3xl font-bold text-slate-900 mb-3">Page Title</h1>
            <p className="text-slate-600 mb-6 leading-relaxed">
              Connect to Ponder indexer to view data
            </p>
            <Badge variant="outline" className="text-yellow-700 border-yellow-300">
              Not Connected
            </Badge>
          </div>
        </div>
      </div>
    )
  }
 
  // Page content...
}
 
// 2. Ownership-Based Security (Project Management)
function ProjectManagementActions() {
  const { isConnected: walletConnected, address } = useAccount()
  const { project, overview } = useProjectDetailData(projectId)
  
  // Security: Verify project ownership for management actions
  const isProjectOwner = useMemo(() => {
    if (!walletConnected || !address || !overview?.owner) {
      return false
    }
    return address.toLowerCase() === overview.owner.toLowerCase()
  }, [walletConnected, address, overview?.owner])
 
  return (
    <div className="bg-white border border-slate-200 space-y-4">
      <div className="p-6 pb-0">
        <div className="flex items-center justify-between">
          <h2 className="text-lg font-semibold flex items-center gap-2">
            <Settings className="h-5 w-5" />
            Project Management
          </h2>
          {isProjectOwner && (
            <Badge variant="default" className="bg-green-100 text-green-800 border-green-200">
              Owner Access
            </Badge>
          )}
        </div>
      </div>
      
      <div className="px-6 pb-6 space-y-3">
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!isProjectOwner || !overview || overview.stage !== 1}
          onClick={() => openModal('updateDetails')}
        >
          <Edit3 className="h-4 w-4 mr-2" />
          Update Project Details
        </Button>
        
        <Button 
          className="w-full justify-start" 
          variant="outline"
          disabled={!isProjectOwner || !overview || overview.stage === 6}
          onClick={() => openModal('updateConfig')}
        >
          <Settings className="h-4 w-4 mr-2" />
          Update Configuration
        </Button>
      </div>
    </div>
  )
}
 
// 3. Role-Based Security (Component Actions)
import { useHasRole } from '@/lib/progressive-role-hooks'
 
function DistributorDepositAction() {
  const { address: connectedAddress, isConnected } = useAccount()
  
  // DISTRIBUTOR_ROLE constant
  const DISTRIBUTOR_ROLE = '0xfbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c'
  
  const { 
    hasRole: hasDistributorRole, 
    isLoading: isCheckingRole, 
    error: roleCheckError 
  } = useHasRole(DISTRIBUTOR_ROLE, connectedAddress || null)
 
  const canDeposit = useMemo(() => {
    return isConnected && hasDistributorRole && !roleCheckError
  }, [isConnected, hasDistributorRole, roleCheckError])
 
  return (
    <div className="space-y-4">
      {/* Role Status Display */}
      {isCheckingRole ? (
        <Alert className="border-blue-200 bg-blue-50">
          <Shield className="h-4 w-4" />
          <AlertDescription>
            <strong>Checking Authorization:</strong> Verifying distributor role permissions...
          </AlertDescription>
        </Alert>
      ) : roleCheckError ? (
        <Alert className="border-red-200 bg-red-50">
          <AlertTriangle className="h-4 w-4" />
          <AlertDescription>
            <strong>Role Check Error:</strong> Unable to verify role permissions. {roleCheckError}
          </AlertDescription>
        </Alert>
      ) : !hasDistributorRole ? (
        <Alert className="border-red-200 bg-red-50">
          <AlertTriangle className="h-4 w-4" />
          <AlertDescription>
            <strong>Insufficient Permissions:</strong> You need the DISTRIBUTOR role to make deposits. 
            Only authorized distributors can deposit project tokens.
          </AlertDescription>
        </Alert>
      ) : (
        <Alert className="border-green-200 bg-green-50">
          <Shield className="h-4 w-4" />
          <AlertDescription>
            <strong>✅ Authorized Distributor:</strong> You have the required DISTRIBUTOR role and can make deposits.
          </AlertDescription>
        </Alert>
      )}
 
      {/* Action Button */}
      <Button
        disabled={!canDeposit}
        onClick={performDepositAction}
        className="w-full"
      >
        {hasDistributorRole ? 'Deposit Tokens' : 'Access Denied'}
      </Button>
    </div>
  )
}

Parameter and Route Security

Dynamic Route Parameter Patterns
// Pattern 1: Address parameter (from examples/app/user/[address]/page.tsx)
import { useParams } from 'next/navigation'
import { isAddress } from 'viem'
import { useSettingsStore } from '@/lib/settings-store'
 
export default function UserProfilePage() {
  const params = useParams()
  const userAddress = params.address as string
  const { isConnected: _isConnected } = useSettingsStore()
  const [isClient, setIsClient] = useState(false)
  
  // Security: Client-side hydration protection pattern
  useEffect(() => {
    setIsClient(true)
  }, [])
 
  const isConnected = useMemo(() => isClient ? _isConnected : false, [isClient, _isConnected])
 
  // Security: Optional parameter validation (recommended for production)
  const isValidAddress = useMemo(() => {
    return userAddress && isAddress(userAddress)
  }, [userAddress])
 
  // Security: Use route parameter directly in hooks with progressive loading
  const { 
    profile, 
    investments, 
    activities, 
    nfts, 
    purchases,
    refunds,
    claims,
    isLoading 
  } = useUserProfileData(userAddress)
 
  // Security: Connection validation before showing user data
  if (!isConnected) {
    return (
      <div className="flex-1 space-y-8 p-8">
        <div className="text-center py-20">
          <div className="bg-gradient-to-br from-blue-50 to-purple-50 p-8 max-w-md mx-auto">
            <User className="mx-auto h-16 w-16 text-slate-400 mb-6" />
            <h1 className="text-3xl font-bold text-slate-900 mb-3">User Profile</h1>
            <p className="text-slate-600 mb-6 leading-relaxed">
              Connect to Ponder indexer to view user data
            </p>
            <Badge variant="outline" className="text-yellow-700 border-yellow-300">
              Not Connected
            </Badge>
          </div>
        </div>
      </div>
    )
  }
 
  // Security: Parameter validation error state (optional)
  if (!isValidAddress) {
    return (
      <div className="flex-1 space-y-8 p-8">
        <div className="text-center py-20">
          <h1 className="text-2xl font-bold text-slate-900 mb-3">Invalid Address</h1>
          <p className="text-slate-600">Please provide a valid Ethereum address</p>
        </div>
      </div>
    )
  }
 
  return (
    <div className="flex-1 space-y-8 p-8">
      <p className="text-lg text-slate-600 mt-1">
        Investment activity and portfolio for <span className="font-mono">{shortenAddress(userAddress)}</span>
      </p>
      {/* User profile content */}
    </div>
  )
}
// Pattern 2: Numeric parameter (from examples/app/project/[id]/page.tsx)
export default function ProjectDetailPage() {
  const params = useParams()
  const { isConnected: _isConnected } = useSettingsStore()
  const [isClient, setIsClient] = useState(false)
 
  // Security: Numeric parameter conversion with validation
  const projectId = useMemo(() => {
    const id = Number(params.id)
    return !isNaN(id) && id > 0 ? id : null
  }, [params.id])
 
  // Security: Client-side hydration protection
  useEffect(() => {
    setIsClient(true)
  }, [])
 
  const isConnected = useMemo(() => isClient ? _isConnected : false, [isClient, _isConnected])
 
  // Security: Use validated parameter in data hooks
  const { project, overview, isLoading, error } = useProjectDetailData(projectId || 0)
 
  // Security: Connection validation
  if (!isConnected) {
    return (
      <div className="flex-1 space-y-8 p-8">
        <div className="text-center py-20">
          <div className="bg-gradient-to-br from-blue-50 to-purple-50 p-8 max-w-md mx-auto">
            <TrendingUp className="mx-auto h-16 w-16 text-slate-400 mb-6" />
            <h1 className="text-3xl font-bold text-slate-900 mb-3">Project Details</h1>
            <p className="text-slate-600 mb-6 leading-relaxed">
              Connect to Ponder indexer to view project information
            </p>
            <Badge variant="outline" className="text-yellow-700 border-yellow-300">
              Not Connected
            </Badge>
          </div>
        </div>
      </div>
    )
  }
 
  // Security: Parameter validation error state
  if (!projectId) {
    return (
      <div className="flex-1 space-y-8 p-8">
        <div className="text-center py-20">
          <h1 className="text-2xl font-bold text-slate-900 mb-3">Invalid Project ID</h1>
          <p className="text-slate-600">Please provide a valid numeric project ID</p>
        </div>
      </div>
    )
  }
 
  // Security: Data validation before rendering
  if (error || !project || !overview) {
    return (
      <div className="flex-1 space-y-8 p-8">
        <div className="text-center py-20">
          <h1 className="text-2xl font-bold text-slate-900 mb-3">Project Not Found</h1>
          <p className="text-slate-600">
            The project you're looking for doesn't exist or hasn't been indexed yet.
          </p>
        </div>
      </div>
    )
  }
 
  return (
    <div className="flex-1 space-y-8 p-8">
      <h1 className="text-4xl font-bold tracking-tight text-slate-900">
        #{overview.projectId}
      </h1>
      {/* Project content */}
    </div>
  )
}
// Pattern 3: Static routes (from examples/app/ifif-projects/page.tsx)
export default function IFIFProjectsPage() {
  const router = useRouter()
  const [isClient, setIsClient] = useState(false)
  const { isConnected: _isConnected } = useSettingsStore()
 
  // Security: Client-side hydration protection (still required)
  useEffect(() => {
    setIsClient(true)
  }, [])
 
  const isConnected = useMemo(() => isClient ? _isConnected : false, [isClient, _isConnected])
 
  // Security: Connection validation for data access
  if (!isConnected) {
    return (
      <div className="flex-1 space-y-8 p-8">
        <div className="text-center py-20">
          <div className="bg-gradient-to-br from-blue-50 to-purple-50 p-8 max-w-md mx-auto">
            <Folder className="mx-auto h-16 w-16 text-slate-400 mb-6" />
            <h1 className="text-3xl font-bold text-slate-900 mb-3">IFIF Projects</h1>
            <p className="text-slate-600 mb-6 leading-relaxed">
              Connect to Ponder indexer to view project data
            </p>
            <Badge variant="outline" className="text-yellow-700 border-yellow-300">
              Not Connected
            </Badge>
          </div>
        </div>
      </div>
    )
  }
 
  // Navigation with parameter passing
  const handleProjectSelect = useCallback((projectId: string) => {
    router.push(`/project/${projectId}`)
  }, [router])
 
  return (
    <div className="flex-1 space-y-8 p-8">
      {/* Static route content */}
    </div>
  )
}
Key Security Principles:
  1. Parameter Validation: Always validate route parameters before use
  2. Type Conversion: Use proper type conversion with validation for numeric parameters
  3. Hydration Protection: Implement client-side hydration protection for all routes
  4. Connection Validation: Verify data source connection before displaying content
  5. Error Boundaries: Provide meaningful error states for invalid parameters
  6. Progressive Loading: Use validated parameters safely in data fetching hooks

Performance Optimization

Progressive Data Loading Architecture
// Pattern 1: Progressive Loading Hook (from examples/lib/progressive-ifif-hooks.ts)
import { useDataCacheStore } from './data-cache-store'
import { usePonderQuery } from '@ponder/react'
 
export const useProgressiveIFIFProjectsLoader = () => {
  const [currentBatch, setCurrentBatch] = useState(0)
  const [isLoadingMore, setIsLoadingMore] = useState(false)
  const [allProjects, setAllProjects] = useState<IFIFProject[]>([])
  
  const updateProgress = useDataCacheStore((state) => state.updateProgress)
  const setProjectsData = useDataCacheStore((state) => state.setIFIFProjectsData)
 
  const batchSize = IFIF_PROGRESSIVE_CONFIG.projects.batchSize
 
  // Get total count for progress calculation
  const { data: totalCountData } = usePonderQuery({
    queryFn: (db) => db.select({ count: count() }).from(project),
  })
 
  // Load data in batches
  const { data: batchData, isLoading } = usePonderQuery({
    queryFn: (db) => 
      db.select().from(project)
        .orderBy(desc(project.createdAt))
        .limit(batchSize)
        .offset(currentBatch * batchSize),
  })
 
  // Progressive loading effect
  useEffect(() => {
    if (batchData && batchData.length > 0 && !isLoading) {
      setAllProjects(prev => {
        const combined = [...prev, ...batchData]
        const unique = combined.filter((item, index, self) => 
          index === self.findIndex(t => t.id === item.id)
        )
        return unique
      })
      setIsLoadingMore(false)
    }
  }, [batchData, isLoading])
 
  // Cache management effect
  useEffect(() => {
    const totalCount = totalCountData?.[0]?.count || 0
    if (allProjects.length > 0) {
      updateProgress('ififProjects', {
        current: allProjects.length,
        total: totalCount,
        percentage: totalCount ? Math.round((allProjects.length / totalCount) * 100) : 0,
        isComplete: allProjects.length >= totalCount,
        isLoading: isLoading || isLoadingMore
      })
      setProjectsData(allProjects)
    }
  }, [allProjects, totalCountData, isLoading, isLoadingMore, updateProgress, setProjectsData])
 
  return {
    projects: allProjects,
    isLoading: isLoading || isLoadingMore,
    progress: allProjects.length,
    total: totalCountData?.[0]?.count || 0
  }
}
Advanced Caching with Zustand Store
// Pattern 2: Data Cache Store (from examples/lib/data-cache-store.ts)
import { create } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'
 
interface DataCacheState {
  // Cached data
  ififProjects: IFIFProject[]
  operations: NFTOperation[]
  purchases: UserPurchase[]
  
  // Progress tracking
  ififProjectsProgress: ProgressState
  operationsProgress: ProgressState
  
  // Actions
  setIFIFProjectsData: (data: IFIFProject[]) => void
  updateProgress: (type: string, progress: Partial<ProgressState>) => void
  getTotalProgress: () => number
  isAnyLoading: () => boolean
}
 
export const useDataCacheStore = create<DataCacheState>()(
  subscribeWithSelector((set, get) => ({
    // Initial state
    ififProjects: [],
    operations: [],
    purchases: [],
    
    ififProjectsProgress: { current: 0, total: 0, percentage: 0, isComplete: false, isLoading: false },
    operationsProgress: { current: 0, total: 0, percentage: 0, isComplete: false, isLoading: false },
    
    // Update cached data
    setIFIFProjectsData: (data) => set({ ififProjects: data }),
    
    // Update progress tracking
    updateProgress: (type, progress) => set((state) => ({
      [`${type}Progress`]: { ...state[`${type}Progress` as keyof DataCacheState], ...progress }
    })),
    
    // Calculate overall progress across all data types
    getTotalProgress: () => {
      const state = get()
      const allProgress = [
        state.ififProjectsProgress.percentage,
        state.operationsProgress.percentage,
        // Add other progress states...
      ]
      return Math.round(allProgress.reduce((sum, p) => sum + p, 0) / allProgress.length)
    },
    
    // Check if any data loading is in progress
    isAnyLoading: () => {
      const state = get()
      return state.ififProjectsProgress.isLoading || 
             state.operationsProgress.isLoading
    }
  }))
)
Project Detail Page Performance Optimizations
// Pattern 3: Project Detail Performance (from examples/app/project/[id]/page.tsx)
export default function ProjectDetailPage() {
  const params = useParams()
  const [isClient, setIsClient] = useState(false)
  
  // Performance: Memoized parameter validation
  const projectId = useMemo(() => {
    const id = Number(params.id)
    return !isNaN(id) && id > 0 ? id : null
  }, [params.id])
 
  // Performance: Progressive data loading
  const { project, overview, isLoading, error } = useProjectDetailData(projectId || 0)
  const { projects: ififProjects } = useCachedIFIFData()
 
  // Performance: Memoized project owner check
  const isProjectOwner = useMemo(() => {
    if (!walletConnected || !address || !overview?.owner) {
      return false
    }
    return address.toLowerCase() === overview.owner.toLowerCase()
  }, [walletConnected, address, overview?.owner])
 
  // Performance: Memoized activity data aggregation
  const activitiesData = useMemo(() => {
    let activities: ActivityData[] = []
    
    // Filter and combine data from cached collections
    if (deployments) activities.push(...deployments.filter(d => Number(d.projectId) === projectId).map(d => ({ ...d, type: 'deployment' } as ActivityData)))
    if (claims) activities.push(...claims.filter(c => c.projectAddress === overview?.projectAddress).map(c => ({ ...c, type: 'claim' } as ActivityData)))
    if (operations) activities.push(...operations.filter(op => op.projectAddress === overview?.projectAddress).map(op => ({ ...op, type: 'operation' } as ActivityData)))
    
    // Performance: Sort once with optimized comparison
    return activities.sort((a, b) => {
      const timestampA = typeof a.timestamp === 'string' ? parseFloat(a.timestamp) : Number(a.timestamp)
      const timestampB = typeof b.timestamp === 'string' ? parseFloat(b.timestamp) : Number(b.timestamp)
      return timestampB - timestampA
    })
  }, [deployments, claims, operations, purchases, refunds, deposits, projectId, overview?.projectAddress])
 
  // Performance: Memoized user data calculations
  const userData = useMemo(() => {
    if (!address || !overview?.projectAddress) {
      return { hasInvestment: false, investmentAmount: 0n, canRefund: false, canClaim: false }
    }
 
    const userInvestment = userInvestments?.find(inv => 
      inv.projectAddress.toLowerCase() === overview.projectAddress.toLowerCase()
    )
    
    return {
      hasInvestment: !!userInvestment,
      investmentAmount: BigInt(userInvestment?.totalInvested || '0'),
      canRefund: overview.stage === 2 || overview.stage === 3, // During sales
      canClaim: overview.stage === 6, // After completion
    }
  }, [address, overview?.projectAddress, overview?.stage, userInvestments])
 
  return (
    <div className="flex-1 space-y-8 p-8">
      {/* Optimized content rendering */}
    </div>
  )
}
Project Investor Page Performance Patterns
// Pattern 4: Investor Page Optimizations (from examples/app/project/[id]/investors/page.tsx)
export default function ProjectInvestorsPage() {
  const params = useParams()
  const projectId = params.id as string
  
  const { project } = useProjectDetailData(parseInt(projectId) || 0)
  const { investors, purchases, refunds, claims, isLoading } = useProjectInvestorData(project?.id || '')
 
  // Performance: Memoized metric calculations with BigInt optimization
  const totalInvestors = useMemo(() => investors.length, [investors.length])
  
  const totalInvested = useMemo(() => 
    purchases.reduce((sum: bigint, p: UserPurchase) => sum + BigInt(p.amount), BigInt(0)), 
    [purchases]
  )
  
  const totalRefunded = useMemo(() => 
    refunds.reduce((sum: bigint, r: UserRefund) => sum + BigInt(r.amount), BigInt(0)), 
    [refunds]
  )
  
  const totalClaimed = useMemo(() => 
    claims.reduce((sum: bigint, c: TokenClaim) => sum + BigInt(c.amount), BigInt(0)), 
    [claims]
  )
  
  const netInvestment = useMemo(() => 
    totalInvested - totalRefunded, 
    [totalInvested, totalRefunded]
  )
 
  // Performance: Memoized metrics array for MetricGrid
  const investorMetrics = useMemo(() => [
    {
      key: 'users' as const,
      value: totalInvestors,
      subtitle: 'Total investors'
    },
    {
      key: 'totalFunding' as const,
      value: parseFloat(formatEther(totalInvested)).toFixed(2),
      subtitle: 'Total invested'
    },
    {
      key: 'activeProjects' as const,
      value: parseFloat(formatEther(netInvestment)).toFixed(2),
      subtitle: 'Net investment'
    },
    {
      key: 'last24h' as const,
      value: purchases.length + refunds.length + claims.length,
      subtitle: 'Total transactions'
    }
  ], [totalInvestors, totalInvested, netInvestment, purchases.length, refunds.length, claims.length])
 
  return (
    <div className="flex-1 space-y-8 p-8">
      <MetricGrid metrics={investorMetrics} />
      {/* Performance-optimized table rendering */}
    </div>
  )
}
NFT Management Performance Optimizations
// Pattern 5: NFT Page Performance (from examples/app/project/[id]/nft/page.tsx)
export default function ProjectNFTPage() {
  const params = useParams()
  const projectId = params.id as string
  
  const { project } = useProjectDetailData(parseInt(projectId) || 0)
  const { allocations, operations, isLoading } = useProjectNFTData(project?.id || '')
 
  // Performance: Complex metrics calculation with single memoization
  const { totalNFTs, activeNFTs, totalOperations, uniqueHolders } = useMemo(() => {
    const totalNFTs = allocations?.length || 0
    const activeNFTs = allocations?.filter((nft: any) => nft.isActive).length || 0
    const totalOperations = operations?.length || 0
    const uniqueHolders = new Set(allocations?.map((nft: any) => nft.owner)).size || 0
 
    return {
      totalNFTs,
      activeNFTs,
      totalOperations,
      uniqueHolders
    }
  }, [allocations, operations])
 
  // Performance: Memoized metrics for UI components
  const nftMetrics = useMemo(() => [
    {
      key: 'totalActivities' as const,
      value: totalNFTs,
      subtitle: 'Total NFTs minted'
    },
    {
      key: 'activities' as const,
      value: activeNFTs,
      subtitle: 'Currently active'
    },
    {
      key: 'users' as const,
      value: uniqueHolders,
      subtitle: 'Unique holders'
    },
    {
      key: 'last24h' as const,
      value: totalOperations,
      subtitle: 'Total operations'
    }
  ], [totalNFTs, activeNFTs, uniqueHolders, totalOperations])
 
  return (
    <div className="flex-1 space-y-8 p-8">
      <MetricGrid metrics={nftMetrics} />
      {/* Performance-optimized NFT rendering */}
    </div>
  )
}
Performance Monitoring & Analytics
// Pattern 6: Performance Monitoring (from examples/lib/progressive-analytics-hooks.ts)
export const usePerformanceMonitoring = () => {
  const { getTotalProgress, isAnyLoading } = useDataCacheStore()
  
  // Performance: Track loading states across all data types
  const overallProgress = useMemo(() => ({
    percentage: getTotalProgress(),
    isLoading: isAnyLoading(),
    isInitialized: getTotalProgress() > 0
  }), [getTotalProgress, isAnyLoading])
 
  // Performance: Monitor component render cycles
  const renderMetrics = useRef({
    renderCount: 0,
    lastRender: Date.now()
  })
 
  useEffect(() => {
    renderMetrics.current.renderCount++
    renderMetrics.current.lastRender = Date.now()
  })
 
  return {
    overallProgress,
    renderMetrics: renderMetrics.current
  }
}
Key Performance Principles:
  1. Progressive Loading: Load data in chunks to prevent UI blocking
  2. Intelligent Caching: Use Zustand store for cross-component data sharing
  3. Memoization Strategy: Optimize expensive calculations with useMemo
  4. Batched Updates: Minimize re-renders with batched state updates
  5. BigInt Optimization: Efficient handling of large numbers in calculations
  6. Component-Level Optimization: Isolate performance-critical calculations
  7. Progress Tracking: Real-time monitoring of data loading progress

The IFIF Project Management System demonstrates a complete Web3 application with enterprise-grade features, providing developers with practical examples of implementing complex DeFi functionality while maintaining security and performance standards.