Skip to content
Open
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
5 changes: 4 additions & 1 deletion src/app/compare/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ArrowLeft, Share2, Download, Clock, Trash2, FileText } from 'lucide-rea
import { propertyService } from '@/lib/propertyService';
import { useComparisonHistoryStore } from '@/store/comparisonHistoryStore';
import { useComparisonStore } from '@/store/comparisonStore';
import { withRouteErrorBoundary } from '@/components/error/withRouteErrorBoundary';
import type { Property } from '@/types/property';
import { formatPrice, formatROI } from '@/utils/searchUtils';

Expand Down Expand Up @@ -93,7 +94,7 @@ function getBestValue(properties: Property[], metric: ComparisonMetric): number
return metric.higherIsBetter ? Math.max(...numericValues) : Math.min(...numericValues);
}

export default function ComparePage() {
function ComparePage() {
const router = useRouter();
const searchParams = useSearchParams();
const { selectedProperties, clearProperties } = useComparisonStore();
Expand Down Expand Up @@ -403,3 +404,5 @@ export default function ComparePage() {
</div>
);
}

export default withRouteErrorBoundary(ComparePage, { routeName: 'compare' });
5 changes: 4 additions & 1 deletion src/app/developers/tokenize/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { withRouteErrorBoundary } from '@/components/error/withRouteErrorBoundary';
import {
Building2,
Coins,
Expand Down Expand Up @@ -32,7 +33,7 @@ const STEPS = [
{ id: 5, title: 'Review & Submit', icon: CheckCircle2 },
];

export default function TokenizationWizardPage() {
function TokenizationWizardPage() {
const [currentStep, setCurrentStep] = useState(1);
const [formData, setFormData] = useState({
name: '',
Expand Down Expand Up @@ -382,3 +383,5 @@ export default function TokenizationWizardPage() {
</div>
);
}

export default withRouteErrorBoundary(TokenizationWizardPage, { routeName: 'developers/tokenize' });
5 changes: 4 additions & 1 deletion src/app/governance/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React, { useState, useEffect, useCallback } from 'react';
import Link from 'next/link';
import { WalletConnector } from '@/components/WalletConnector';
import { withRouteErrorBoundary } from '@/components/error/withRouteErrorBoundary';

// ─── Types ────────────────────────────────────────────────────────────────────

Expand Down Expand Up @@ -235,7 +236,7 @@ function ProposalCard({

// ─── Page ─────────────────────────────────────────────────────────────────────

export default function GovernancePage() {
function GovernancePage() {
const [proposals, setProposals] = useState<Proposal[]>(MOCK_PROPOSALS);
const [userVotes, setUserVotes] = useState<Record<string, VoteChoice>>({});
const [filter, setFilter] = useState<'all' | Proposal['status']>('all');
Expand Down Expand Up @@ -331,3 +332,5 @@ export default function GovernancePage() {
</div>
);
}

export default withRouteErrorBoundary(GovernancePage, { routeName: 'governance' });
5 changes: 4 additions & 1 deletion src/app/mobile-properties/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import Image from "next/image";
import { withRouteErrorBoundary } from '@/components/error/withRouteErrorBoundary';
import {
Search,
MapPin,
Expand Down Expand Up @@ -211,7 +212,7 @@ const properties: MobileProperty[] = [
},
];

export default function MobilePropertiesPage() {
function MobilePropertiesPage() {
const [activeTab, setActiveTab] = useState("browse");
const [searchQuery, setSearchQuery] = useState("");
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
Expand Down Expand Up @@ -463,6 +464,8 @@ export default function MobilePropertiesPage() {
);
}

export default withRouteErrorBoundary(MobilePropertiesPage, { routeName: 'mobile-properties' });

function SectionSkeleton() {
return (
<div className="p-4">
Expand Down
6 changes: 4 additions & 2 deletions src/app/secondary-market/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { SecondaryMarketListing } from '@/types/property';
import { WalletConnector } from '@/components/WalletConnector';
import { Button } from '@/components/ui/button';
import { LoadingSpinner } from '@/components/LoadingSpinner';
import { CardSkeleton } from '@/components/ui/LoadingSkeletons';
import { withRouteErrorBoundary } from '@/components/error/withRouteErrorBoundary';
import Link from 'next/link';
import Image from 'next/image';
import { toast } from 'sonner';

export default function SecondaryMarketPage() {
function SecondaryMarketPage() {
const [listings, setListings] = useState<SecondaryMarketListing[]>([]);
const [isLoading, setIsLoading] = useState(true);

Expand Down Expand Up @@ -135,3 +135,5 @@ export default function SecondaryMarketPage() {
</div>
);
}

export default withRouteErrorBoundary(SecondaryMarketPage, { routeName: 'secondary-market' });
5 changes: 4 additions & 1 deletion src/app/tax-report/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React, { useState } from 'react';
import Link from 'next/link';
import { WalletConnector } from '@/components/WalletConnector';
import { withRouteErrorBoundary } from '@/components/error/withRouteErrorBoundary';

// ─── Types ────────────────────────────────────────────────────────────────────

Expand Down Expand Up @@ -180,7 +181,7 @@ async function generatePDF(

// ─── Component ────────────────────────────────────────────────────────────────

export default function TaxReportPage() {
function TaxReportPage() {
const [taxYear, setTaxYear] = useState('2024');
const [method, setMethod] = useState<CostBasisMethod>('FIFO');
const [generating, setGenerating] = useState(false);
Expand Down Expand Up @@ -358,3 +359,5 @@ export default function TaxReportPage() {
</div>
);
}

export default withRouteErrorBoundary(TaxReportPage, { routeName: 'tax-report' });
5 changes: 4 additions & 1 deletion src/app/widget/investment-calculator/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'next/navigation';
import { InvestmentCalculatorWidget } from '@/components/widget/InvestmentCalculatorWidget';
import { withRouteErrorBoundary } from '@/components/error/withRouteErrorBoundary';

export default function InvestmentCalculatorEmbedPage() {
function InvestmentCalculatorEmbedPage() {
const searchParams = useSearchParams();
const [mounted, setMounted] = useState(false);

Expand Down Expand Up @@ -46,3 +47,5 @@ export default function InvestmentCalculatorEmbedPage() {
</div>
);
}

export default withRouteErrorBoundary(InvestmentCalculatorEmbedPage, { routeName: 'widget/investment-calculator' });
14 changes: 7 additions & 7 deletions src/components/WalletModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getWalletErrorMessage } from '@/utils/errorHandling';
import { toChainId } from '@/config/chains';
import { useSecurity } from '@/hooks/useSecurity';
import { useWalletConnector } from '@/hooks/useWalletConnector';
import { AlertTriangle, Shield, X, CheckCircle, AlertCircle, Loader2 } from 'lucide-react';
import { AlertTriangle, Shield, X, CheckCircle, AlertCircle, Loader2, Wallet, Link2, QrCode } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { ModalTransition } from './PageTransition';

Expand Down Expand Up @@ -199,31 +199,31 @@ export const WalletModal: React.FC<WalletModalProps> = ({ isOpen, onClose }) =>
id: SupportedWalletId;
name: string;
description: string;
icon: string;
icon: React.ReactNode;
color: string;
installUrl?: string;
}> = [
{
id: 'metamask',
name: 'MetaMask',
description: 'Connect to your MetaMask wallet',
icon: '🦊',
icon: <Wallet className="w-6 h-6" aria-hidden="true" />,
color: 'bg-orange-500',
installUrl: 'https://metamask.io/download/',
},
{
id: 'coinbase',
name: 'Coinbase Wallet',
description: 'Connect to your Coinbase wallet',
icon: '�',
icon: <QrCode className="w-6 h-6" aria-hidden="true" />,
color: 'bg-blue-600',
installUrl: 'https://www.coinbase.com/wallet',
},
{
id: 'walletconnect',
name: 'WalletConnect',
description: 'Connect with WalletConnect',
icon: '�',
icon: <Link2 className="w-6 h-6" aria-hidden="true" />,
color: 'bg-blue-500',
},
].sort((a, b) => {
Expand Down Expand Up @@ -303,7 +303,7 @@ export const WalletModal: React.FC<WalletModalProps> = ({ isOpen, onClose }) =>
disabled={isConnecting || isLoadingConnector}
className="w-full flex items-center gap-4 p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
<div className={`w-12 h-12 ${wallet.color} rounded-lg flex items-center justify-center text-white text-xl`}>
<div className={`w-12 h-12 ${wallet.color} rounded-lg flex items-center justify-center text-white`} title={wallet.name} aria-label={`${wallet.name} wallet`}>
{wallet.icon}
</div>
<div className="flex-1 text-left">
Expand All @@ -330,7 +330,7 @@ export const WalletModal: React.FC<WalletModalProps> = ({ isOpen, onClose }) =>
key={wallet.id}
className="w-full flex items-center gap-4 p-4 border border-gray-200 dark:border-gray-700 rounded-lg"
>
<div className={`w-12 h-12 ${wallet.color} rounded-lg flex items-center justify-center text-white text-xl opacity-60`}>
<div className={`w-12 h-12 ${wallet.color} rounded-lg flex items-center justify-center text-white opacity-60`} title={wallet.name} aria-label={`${wallet.name} wallet - not installed`}>
{wallet.icon}
</div>
<div className="flex-1 text-left">
Expand Down
24 changes: 22 additions & 2 deletions src/components/dashboard/DataRefreshWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useCallback } from "react";
import { useState, useCallback, useRef, useEffect } from "react";
import type { ReactNode } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { RefreshCw, AlertCircle, CheckCircle } from "lucide-react";
Expand All @@ -21,8 +21,25 @@ 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 handleRefresh = useCallback(async () => {
// Cancel any pending success timeout from a previous refresh
if (successTimerRef.current) {
clearTimeout(successTimerRef.current);
successTimerRef.current = null;
}

setRefreshState("loading");
setError(null);

Expand All @@ -44,7 +61,10 @@ export const DataRefreshWrapper = ({
}

setRefreshState("success");
setTimeout(() => setRefreshState("idle"), 2000);
successTimerRef.current = setTimeout(() => {
setRefreshState("idle");
successTimerRef.current = null;
}, 2000);
} catch (err) {
setError(err instanceof Error ? err.message : "An error occurred");
setRefreshState("error");
Expand Down
Loading