Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 64 additions & 38 deletions src/app/compare/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,45 +151,71 @@ function ComparePage() {
const printWindow = window.open('', '_blank');
if (!printWindow) return;

const html = `
<!DOCTYPE html>
<html>
<head>
<title>Property Comparison</title>
<style>
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; } }
</style>
</head>
<body>
<h1>Property Comparison Report</h1>
<p>Generated on ${new Date().toLocaleDateString()}</p>
<table>
<thead>
<tr>
<th>Metric</th>
${properties.map(p => `<th>${p.name}</th>`).join('')}
</tr>
</thead>
<tbody>
${comparisonMetrics.map(metric => `
<tr>
<td>${metric.label}</td>
${properties.map(p => `<td>${metric.format(getNestedValue(p, metric.key), p)}</td>`).join('')}
</tr>
`).join('')}
</tbody>
</table>
</body>
</html>
const doc = printWindow.document;
doc.open();
doc.write('<!DOCTYPE html>');

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();
};

Expand Down
3 changes: 2 additions & 1 deletion src/components/LoadingSpinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,12 @@ interface SkeletonProps {
}

export const Skeleton: React.FC<SkeletonProps> = ({ className = '', lines = 1 }) => {
const id = React.useId();
return (
<div role="status" aria-busy="true" aria-label="Loading content" className={`space-y-2 ${className}`}>
{Array.from({ length: lines }).map((_, index) => (
<div
key={index}
key={`${id}-skeleton-line-${index}`}
className="h-4 bg-gray-200 dark:bg-gray-700 rounded animate-pulse"
style={{
width: `${Math.random() * 40 + 60}%`,
Expand Down
4 changes: 2 additions & 2 deletions src/components/SecureTransactionConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ export const SecureTransactionConfirmation: React.FC<SecureTransactionConfirmati
Warnings
</p>
{validation.warnings.map((warning: string, index: number) => (
<p key={index} className="text-xs text-yellow-700 dark:text-yellow-300">
<p key={`warn-${index}`} className="text-xs text-yellow-700 dark:text-yellow-300">
• {warning}
</p>
))}
Expand All @@ -254,7 +254,7 @@ export const SecureTransactionConfirmation: React.FC<SecureTransactionConfirmati
Risk Factors
</p>
{validation.risks.map((risk: string, index: number) => (
<p key={index} className="text-xs text-orange-700 dark:text-orange-300">
<p key={`risk-${index}`} className="text-xs text-orange-700 dark:text-orange-300">
• {risk}
</p>
))}
Expand Down
20 changes: 7 additions & 13 deletions src/components/TransactionConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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';
Expand Down Expand Up @@ -96,7 +93,6 @@ export const TransactionConfirmation: React.FC<TransactionConfirmationProps> = (
logTransactionScreening(valueEth, requiresKyc, profile.status === 'verified' || !requiresKyc);
}
}, [isOpen, transaction, profile.status, profile.thresholdEth, logTransactionScreening]);
}, [isOpen, transaction.to, transaction.value, transaction.data]);

useEffect(() => {
if (!isOpen) {
Expand Down Expand Up @@ -370,7 +366,7 @@ export const TransactionConfirmation: React.FC<TransactionConfirmationProps> = (
</h4>
<div className="space-y-1">
{validation.warnings.map((warning: string, index: number) => (
<p key={index} className="text-sm text-yellow-700 dark:text-yellow-300">
<p key={`warn-${index}`} className="text-sm text-yellow-700 dark:text-yellow-300">
• {warning}
</p>
))}
Expand All @@ -390,7 +386,7 @@ export const TransactionConfirmation: React.FC<TransactionConfirmationProps> = (
</h4>
<div className="space-y-1">
{validation.blocks.map((block: string, index: number) => (
<p key={index} className="text-sm text-red-700 dark:text-red-300">
<p key={`block-${index}`} className="text-sm text-red-700 dark:text-red-300">
• {block}
</p>
))}
Expand Down Expand Up @@ -537,7 +533,7 @@ export const TransactionConfirmation: React.FC<TransactionConfirmationProps> = (
>
<InputOTPGroup>
{Array.from({ length: 6 }, (_, index) => (
<InputOTPSlot key={index} index={index} />
<InputOTPSlot key={`otp-slot-${index}`} index={index} />
))}
</InputOTPGroup>
</InputOTP>
Expand Down Expand Up @@ -627,8 +623,6 @@ export const TransactionConfirmation: React.FC<TransactionConfirmationProps> = (
>
<X className="w-4 h-4" />
{kycRequired && profile.status !== 'verified' ? 'Complete KYC' : 'Transaction Blocked'}
<X className="h-4 w-4" />
Transaction Blocked
</button>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/audit/TransactionAuditTrail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ export const TransactionAuditTrail: React.FC<TransactionAuditTrailProps> = ({ cl
<AlertTriangle className="h-4 w-4 text-yellow-600 dark:text-yellow-400 mt-0.5" />
<div className="space-y-1">
{entry.warnings.map((warning, index) => (
<p key={index} className="text-xs text-yellow-700 dark:text-yellow-300">
<p key={`${entry.id}-warn-${index}`} className="text-xs text-yellow-700 dark:text-yellow-300">
• {warning}
</p>
))}
Expand Down
19 changes: 5 additions & 14 deletions src/components/dashboard/DataRefreshWrapper.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -21,17 +21,7 @@ export const DataRefreshWrapper = ({
}: DataRefreshWrapperProps) => {
const [refreshState, setRefreshState] = useState<RefreshState>("idle");
const [error, setError] = useState<string | null>(null);
const successTimerRef = useRef<ReturnType<typeof setTimeout> | 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
Expand All @@ -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"));
Expand Down Expand Up @@ -142,7 +133,7 @@ export const DataRefreshWrapper = ({
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{Array.from({ length: 4 }).map((_, i) => (
<div
key={i}
key={`${id}-skeleton-card-${i}`}
className="glass-card rounded-xl p-6 border border-border/50 bg-white/60 dark:bg-gray-900/40"
>
<div className="flex items-start justify-between gap-4">
Expand Down
4 changes: 2 additions & 2 deletions src/components/dashboard/PortfolioReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,9 @@ export const PortfolioReport = () => {
</div>

<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{reportCards.map((item, index) => (
{reportCards.map((item) => (
<div
key={index}
key={item.value}
className="p-4 rounded-lg bg-muted/30 border border-border/50 hover:border-primary/30 transition-colors cursor-pointer"
onClick={() => setReportType(item.value)}
>
Expand Down
2 changes: 1 addition & 1 deletion src/components/dashboard/PropertyCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions src/components/dashboard/RiskAnalysis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ export const RiskAnalysis = () => {
<TrendingUp className="w-4 h-4 text-primary" />
<h4 className="font-medium">Risk Metrics</h4>
</div>
{riskMetrics.map((metric, index) => (
<RiskMeter key={index} metric={metric} />
{riskMetrics.map((metric) => (
<RiskMeter key={metric.label} metric={metric} />
))}
</div>

Expand All @@ -152,8 +152,8 @@ export const RiskAnalysis = () => {
<h4 className="font-medium">Top Holdings Concentration</h4>
</div>
<div className="glass-card rounded-lg p-4">
{concentrationData.map((item, index) => (
<ConcentrationItem key={index} {...item} />
{concentrationData.map((item) => (
<ConcentrationItem key={item.name} {...item} />
))}
</div>
<div className="mt-4 p-3 rounded-lg bg-warning/10 border border-warning/20">
Expand Down
4 changes: 2 additions & 2 deletions src/components/mobile/MobilePropertyCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ export const MobilePropertyCard = ({
{/* Amenities Preview */}
{property.amenities && property.amenities.length > 0 && (
<div className="flex flex-wrap gap-1">
{property.amenities.slice(0, 2).map((amenity, index) => (
<Badge key={index} variant="outline" className="text-xs">
{property.amenities.slice(0, 2).map((amenity) => (
<Badge key={amenity} variant="outline" className="text-xs">
{amenity}
</Badge>
))}
Expand Down
6 changes: 3 additions & 3 deletions src/components/mobile/MobilePropertyViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export const MobilePropertyViewer = ({
<div className="flex gap-2 overflow-x-auto pb-2 mb-4">
{property.images.map((image, index) => (
<button
key={index}
key={image}
onClick={() => setCurrentImageIndex(index)}
className={`flex-shrink-0 w-16 h-16 rounded-lg overflow-hidden border-2 ${
index === currentImageIndex
Expand Down Expand Up @@ -341,9 +341,9 @@ export const MobilePropertyViewer = ({

{property.amenities && (
<div className="flex flex-wrap gap-2">
{property.amenities.slice(0, 3).map((amenity, index) => (
{property.amenities.slice(0, 3).map((amenity) => (
<Badge
key={index}
key={amenity}
variant="secondary"
className="text-xs"
>
Expand Down
4 changes: 2 additions & 2 deletions src/components/mobile/OfflinePropertyCache.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,9 @@ export const OfflinePropertyCache = ({
Cache Health Issues
</h4>
<ul className="mt-1 space-y-1">
{healthIssues.map((issue, index) => (
{healthIssues.map((issue) => (
<li
key={index}
key={issue}
className="text-sm text-orange-700 dark:text-orange-300"
>
{issue}
Expand Down
2 changes: 1 addition & 1 deletion src/components/property/ImageLightbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const ImageLightbox: React.FC<ImageLightboxProps> = ({
<div className="absolute bottom-20 left-1/2 -translate-x-1/2 flex gap-2 max-w-[90vw] overflow-x-auto px-4">
{images.map((image, index) => (
<button
key={index}
key={image}
onClick={() => handleThumbnailClick(index)}
className={`flex-shrink-0 relative rounded-lg overflow-hidden border-2 transition-all ${
index === currentIndex
Expand Down
3 changes: 2 additions & 1 deletion src/components/responsive/ImagePlaceholder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ export const Skeleton: React.FC<SkeletonProps> = ({
const widthStyle = typeof width === 'number' ? `${width}px` : width;
const heightStyle = typeof height === 'number' ? `${height}px` : height;
const radiusStyle = typeof borderRadius === 'number' ? `${borderRadius}px` : borderRadius;
const id = React.useId();

if (lines === 1) {
return (
Expand Down Expand Up @@ -391,7 +392,7 @@ export const Skeleton: React.FC<SkeletonProps> = ({
<div className={className} style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
{Array.from({ length: lines }).map((_, index) => (
<Skeleton
key={index}
key={`${id}-skeleton-line-${index}`}
width={index === lines - 1 ? '80%' : width}
height={height}
borderRadius={borderRadius}
Expand Down
3 changes: 2 additions & 1 deletion src/components/responsive/LazyLoadingExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ export const ContentSkeletonExample: React.FC = () => {
export const PropertyListingExample: React.FC = () => {
const [properties, setProperties] = React.useState<any[]>([]);
const [isLoading, setIsLoading] = React.useState(true);
const loadingId = React.useId();

React.useEffect(() => {
// Preload hero image
Expand Down Expand Up @@ -245,7 +246,7 @@ export const PropertyListingExample: React.FC = () => {
{isLoading ? (
// Show skeleton cards while loading
Array.from({ length: 3 }).map((_, index) => (
<div key={index} className="property-card">
<div key={`${loadingId}-skeleton-${index}`} className="property-card">
<Skeleton width="100%" height="200px" borderRadius="8px" />
<Skeleton width="80%" height="24px" />
<Skeleton width="100%" height="16px" lines={2} />
Expand Down
Loading