diff --git a/src/app/compare/page.tsx b/src/app/compare/page.tsx index 13f8533c..d3416839 100644 --- a/src/app/compare/page.tsx +++ b/src/app/compare/page.tsx @@ -151,45 +151,71 @@ function ComparePage() { const printWindow = window.open('', '_blank'); if (!printWindow) return; - const html = ` - - - - Property Comparison - - - -

Property Comparison Report

-

Generated on ${new Date().toLocaleDateString()}

- - - - - ${properties.map(p => ``).join('')} - - - - ${comparisonMetrics.map(metric => ` - - - ${properties.map(p => ``).join('')} - - `).join('')} - -
Metric${p.name}
${metric.label}${metric.format(getNestedValue(p, metric.key), p)}
- - + const doc = printWindow.document; + doc.open(); + doc.write(''); + + const html = doc.createElement('html'); + + const head = doc.createElement('head'); + const title = doc.createElement('title'); + title.textContent = 'Property Comparison'; + head.appendChild(title); + const style = doc.createElement('style'); + style.textContent = ` + body { font-family: Arial, sans-serif; padding: 20px; } + table { width: 100%; border-collapse: collapse; margin-top: 20px; } + th, td { border: 1px solid #ddd; padding: 12px; text-align: left; } + th { background-color: #f5f5f5; font-weight: bold; } + h1 { color: #333; } + @media print { body { padding: 0; } } `; - - printWindow.document.write(html); - printWindow.document.close(); + head.appendChild(style); + html.appendChild(head); + + const body = doc.createElement('body'); + const h1 = doc.createElement('h1'); + h1.textContent = 'Property Comparison Report'; + body.appendChild(h1); + + const p = doc.createElement('p'); + p.textContent = `Generated on ${new Date().toLocaleDateString()}`; + body.appendChild(p); + + const table = doc.createElement('table'); + + const thead = doc.createElement('thead'); + const headerRow = doc.createElement('tr'); + const metricTh = doc.createElement('th'); + metricTh.textContent = 'Metric'; + headerRow.appendChild(metricTh); + properties.forEach(prop => { + const th = doc.createElement('th'); + th.textContent = prop.name; + headerRow.appendChild(th); + }); + thead.appendChild(headerRow); + table.appendChild(thead); + + const tbody = doc.createElement('tbody'); + comparisonMetrics.forEach(metric => { + const row = doc.createElement('tr'); + const labelCell = doc.createElement('td'); + labelCell.textContent = metric.label; + row.appendChild(labelCell); + properties.forEach(prop => { + const cell = doc.createElement('td'); + cell.textContent = metric.format(getNestedValue(prop, metric.key), prop); + row.appendChild(cell); + }); + tbody.appendChild(row); + }); + table.appendChild(tbody); + body.appendChild(table); + html.appendChild(body); + + doc.documentElement.replaceWith(html); + doc.close(); printWindow.print(); }; diff --git a/src/components/LoadingSpinner.tsx b/src/components/LoadingSpinner.tsx index 2d0d862d..938a06c4 100644 --- a/src/components/LoadingSpinner.tsx +++ b/src/components/LoadingSpinner.tsx @@ -94,11 +94,12 @@ interface SkeletonProps { } export const Skeleton: React.FC = ({ className = '', lines = 1 }) => { + const id = React.useId(); return (
{Array.from({ length: lines }).map((_, index) => (
{validation.warnings.map((warning: string, index: number) => ( -

+

• {warning}

))} @@ -254,7 +254,7 @@ export const SecureTransactionConfirmation: React.FC {validation.risks.map((risk: string, index: number) => ( -

+

• {risk}

))} diff --git a/src/components/TransactionConfirmation.tsx b/src/components/TransactionConfirmation.tsx index 383fa14a..2bec84fa 100644 --- a/src/components/TransactionConfirmation.tsx +++ b/src/components/TransactionConfirmation.tsx @@ -1,14 +1,8 @@ 'use client'; import { logger } from '@/utils/logger'; -import React, { useState } from 'react'; -import Link from 'next/link'; -import { useSecurity } from '@/hooks/useSecurity'; -import { AlertTriangle, Shield, CheckCircle, X, Eye, EyeOff, Info } from 'lucide-react'; -import { useWalletStore } from '@/store/walletStore'; -import { useKycStore } from '@/store/kycStore'; -import { formatEthAmount, shouldRequireKyc, weiToEth } from '@/lib/kyc'; import React, { useEffect, useMemo, useRef, useState } from 'react'; +import Link from 'next/link'; import { useSecurity } from '@/hooks/useSecurity'; import { AlertTriangle, @@ -23,6 +17,9 @@ import { ShieldCheck, Lock, } from 'lucide-react'; +import { useWalletStore } from '@/store/walletStore'; +import { useKycStore } from '@/store/kycStore'; +import { formatEthAmount, shouldRequireKyc, weiToEth } from '@/lib/kyc'; import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/ui/input-otp'; @@ -96,7 +93,6 @@ export const TransactionConfirmation: React.FC = ( logTransactionScreening(valueEth, requiresKyc, profile.status === 'verified' || !requiresKyc); } }, [isOpen, transaction, profile.status, profile.thresholdEth, logTransactionScreening]); - }, [isOpen, transaction.to, transaction.value, transaction.data]); useEffect(() => { if (!isOpen) { @@ -370,7 +366,7 @@ export const TransactionConfirmation: React.FC = (
{validation.warnings.map((warning: string, index: number) => ( -

+

• {warning}

))} @@ -390,7 +386,7 @@ export const TransactionConfirmation: React.FC = (
{validation.blocks.map((block: string, index: number) => ( -

+

• {block}

))} @@ -537,7 +533,7 @@ export const TransactionConfirmation: React.FC = ( > {Array.from({ length: 6 }, (_, index) => ( - + ))} @@ -627,8 +623,6 @@ export const TransactionConfirmation: React.FC = ( > {kycRequired && profile.status !== 'verified' ? 'Complete KYC' : 'Transaction Blocked'} - - Transaction Blocked )}
diff --git a/src/components/audit/TransactionAuditTrail.tsx b/src/components/audit/TransactionAuditTrail.tsx index 91e64480..1bc3fb3c 100644 --- a/src/components/audit/TransactionAuditTrail.tsx +++ b/src/components/audit/TransactionAuditTrail.tsx @@ -369,7 +369,7 @@ export const TransactionAuditTrail: React.FC = ({ cl
{entry.warnings.map((warning, index) => ( -

+

• {warning}

))} diff --git a/src/components/dashboard/DataRefreshWrapper.tsx b/src/components/dashboard/DataRefreshWrapper.tsx index 2e8e9d43..11eb0717 100644 --- a/src/components/dashboard/DataRefreshWrapper.tsx +++ b/src/components/dashboard/DataRefreshWrapper.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback, useRef, useEffect } from "react"; +import { useState, useCallback, useId } from "react"; import type { ReactNode } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { RefreshCw, AlertCircle, CheckCircle } from "lucide-react"; @@ -21,17 +21,7 @@ export const DataRefreshWrapper = ({ }: DataRefreshWrapperProps) => { const [refreshState, setRefreshState] = useState("idle"); const [error, setError] = useState(null); - const successTimerRef = useRef | null>(null); - - // Cleanup all timers on unmount - useEffect(() => { - return () => { - if (successTimerRef.current) { - clearTimeout(successTimerRef.current); - successTimerRef.current = null; - } - }; - }, []); + const id = useId(); const handleRefresh = useCallback(async () => { // Cancel any pending success timeout from a previous refresh @@ -48,7 +38,8 @@ export const DataRefreshWrapper = ({ await new Promise((resolve, reject) => { setTimeout(() => { // 90% success rate for demo - if (Math.random() > 0.1) { + const randomValue = crypto.getRandomValues(new Uint8Array(1))[0] / 256; + if (randomValue > 0.1) { resolve(true); } else { reject(new Error("Failed to fetch latest data")); @@ -142,7 +133,7 @@ export const DataRefreshWrapper = ({
{Array.from({ length: 4 }).map((_, i) => (
diff --git a/src/components/dashboard/PortfolioReport.tsx b/src/components/dashboard/PortfolioReport.tsx index 68dbe200..e2b58b3b 100644 --- a/src/components/dashboard/PortfolioReport.tsx +++ b/src/components/dashboard/PortfolioReport.tsx @@ -267,9 +267,9 @@ export const PortfolioReport = () => {
- {reportCards.map((item, index) => ( + {reportCards.map((item) => (
setReportType(item.value)} > diff --git a/src/components/dashboard/PropertyCard.tsx b/src/components/dashboard/PropertyCard.tsx index 3f6e04aa..454a58a6 100644 --- a/src/components/dashboard/PropertyCard.tsx +++ b/src/components/dashboard/PropertyCard.tsx @@ -32,7 +32,7 @@ export const PropertyCard = ({ property, index }: PropertyCardProps) => { const handlePurchase = () => { // Simulate a transaction hash for demo purposes - const mockTxHash = `0x${Math.random().toString(16).substr(2, 64)}`; + const mockTxHash = `0x${crypto.randomUUID().replace(/-/g, '')}${crypto.randomUUID().replace(/-/g, '')}`; addTransactionToQueue({ hash: mockTxHash, diff --git a/src/components/dashboard/RiskAnalysis.tsx b/src/components/dashboard/RiskAnalysis.tsx index ba65a6fd..28f7b3c4 100644 --- a/src/components/dashboard/RiskAnalysis.tsx +++ b/src/components/dashboard/RiskAnalysis.tsx @@ -140,8 +140,8 @@ export const RiskAnalysis = () => {

Risk Metrics

- {riskMetrics.map((metric, index) => ( - + {riskMetrics.map((metric) => ( + ))}
@@ -152,8 +152,8 @@ export const RiskAnalysis = () => {

Top Holdings Concentration

- {concentrationData.map((item, index) => ( - + {concentrationData.map((item) => ( + ))}
diff --git a/src/components/mobile/MobilePropertyCard.tsx b/src/components/mobile/MobilePropertyCard.tsx index 60a6af93..bafe7996 100644 --- a/src/components/mobile/MobilePropertyCard.tsx +++ b/src/components/mobile/MobilePropertyCard.tsx @@ -231,8 +231,8 @@ export const MobilePropertyCard = ({ {/* Amenities Preview */} {property.amenities && property.amenities.length > 0 && (
- {property.amenities.slice(0, 2).map((amenity, index) => ( - + {property.amenities.slice(0, 2).map((amenity) => ( + {amenity} ))} diff --git a/src/components/mobile/MobilePropertyViewer.tsx b/src/components/mobile/MobilePropertyViewer.tsx index ba8cb31c..91de80ec 100644 --- a/src/components/mobile/MobilePropertyViewer.tsx +++ b/src/components/mobile/MobilePropertyViewer.tsx @@ -259,7 +259,7 @@ export const MobilePropertyViewer = ({
{property.images.map((image, index) => (