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
470 changes: 0 additions & 470 deletions docs/ERROR_HANDLING.md

This file was deleted.

17 changes: 17 additions & 0 deletions src/app/api/transactions/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NextRequest, NextResponse } from 'next/server';
import { getMockApiTransactions } from '@/lib/mockTransactionData';

export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const walletAddress = searchParams.get('walletAddress')?.trim();

if (!walletAddress) {
return NextResponse.json({ error: 'Wallet address required' }, { status: 400 });
}

if (walletAddress.length < 6) {
return NextResponse.json({ error: 'Invalid wallet address' }, { status: 400 });
}

return NextResponse.json(getMockApiTransactions());
}
14 changes: 12 additions & 2 deletions src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ComplianceAuditLog } from "@/components/kyc/ComplianceAuditLog";
import { KycStatusBadge } from "@/components/kyc/KycStatusBadge";
import { useKycStore } from "@/store/kycStore";
import Link from "next/link";
import { useTranslation } from "react-i18next";
import { TransactionSecuritySettings } from "@/components/security/TransactionSecuritySettings";
import { Skeleton } from "@/components/ui/skeleton";

Expand Down Expand Up @@ -61,6 +62,7 @@ const DataRefreshWrapper = dynamic(
);

const Index = () => {
const { t } = useTranslation("common");
const [sidebarOpen, setSidebarOpen] = useState(false);
const [activeItem, setActiveItem] = useState("dashboard");
const { profile } = useKycStore();
Expand Down Expand Up @@ -142,14 +144,22 @@ const Index = () => {
<Tabs defaultValue="queue" className="w-full">
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="queue">Transaction Queue</TabsTrigger>
<TabsTrigger value="history">Transaction History</TabsTrigger>
<TabsTrigger value="history">Transactions</TabsTrigger>
<TabsTrigger value="staking">Staking & Yield</TabsTrigger>
<TabsTrigger value="certificates">My Certificates</TabsTrigger>
</TabsList>
<TabsContent value="queue">
<TransactionQueue />
</TabsContent>
<TabsContent value="history">
<TabsContent value="history" className="space-y-3">
<div className="flex justify-end">
<Link
href="/transactions"
className="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
>
{t("transactions.viewFullHistory")} →
</Link>
</div>
<TransactionHistory />
</TabsContent>
<TabsContent value="certificates">
Expand Down
3 changes: 2 additions & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,10 @@ function HomeContent() {
</ChainAware>

{/* Feature links — Issues #75, #76, #85, #89 */}
<nav aria-label="Platform features" className="mt-12 grid grid-cols-2 sm:grid-cols-4 gap-4">
<nav aria-label="Platform features" className="mt-12 grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-4">
{[
{ href: '/properties', emoji: '🏠', label: 'Browse Properties', desc: 'Shareable property pages with QR codes' },
{ href: '/transactions', emoji: '📜', label: 'Transaction History', desc: 'Search, filter, and export on-chain activity' },
{ href: '/governance', emoji: '🗳️', label: 'Governance', desc: 'Vote on property management decisions' },
{ href: '/tax-report', emoji: '📄', label: 'Tax Reports', desc: 'Form 8949 & Schedule D PDF export' },
{ href: '/accessibility', emoji: '♿', label: 'Accessibility', desc: 'WCAG 2.1 AA compliance demo' },
Expand Down
14 changes: 14 additions & 0 deletions src/app/transactions/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Transaction History | PropChain',
description: 'View, search, and export your on-chain property transactions.',
};

export default function TransactionsLayout({
children,
}: {
children: React.ReactNode;
}) {
return children;
}
14 changes: 14 additions & 0 deletions src/app/transactions/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Skeleton } from '@/components/ui/skeleton';

export default function Loading() {
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 p-6 space-y-6">
<div className="flex justify-between items-center">
<Skeleton className="h-8 w-48" />
<Skeleton className="h-10 w-32" />
</div>
<Skeleton className="h-10 w-64" />
<Skeleton className="h-96 rounded-xl" />
</div>
);
}
72 changes: 72 additions & 0 deletions src/app/transactions/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
'use client';

import React from 'react';
import Link from 'next/link';
import { useAccount } from 'wagmi';
import { useTranslation } from 'react-i18next';
import { TransactionHistory } from '@/components/TransactionHistory';
import { WalletConnector } from '@/components/WalletConnector';
import { ArrowLeft, History } from 'lucide-react';
import { EmptyState } from '@/components/ui/EmptyState';

function TransactionsContent() {
const { t } = useTranslation('common');
const { isConnected } = useAccount();

return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 pb-20 md:pb-8">
<header className="bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700 sticky top-0 z-30">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<div className="flex items-center gap-4">
<Link
href="/dashboard"
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white transition-colors"
>
<ArrowLeft className="w-5 h-5" />
<span className="hidden sm:inline">{t('transactions.backToDashboard')}</span>
</Link>
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center">
<span className="text-white font-bold text-sm">PC</span>
</div>
<h1 className="text-xl font-bold text-gray-900 dark:text-white">PropChain</h1>
</div>
</div>
<WalletConnector />
</div>
</div>
</header>

<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-8">
<div className="flex items-center gap-3 mb-4">
<History className="w-8 h-8 text-blue-600" aria-hidden="true" />
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
{t('transactions.transactionHistory')}
</h1>
</div>
<p className="text-gray-600 dark:text-gray-400">{t('transactions.pageDescription')}</p>
</div>

{!isConnected ? (
<EmptyState
title={t('transactions.connectWallet')}
description={t('transactions.connectWalletDescription')}
icon={History}
action={{
label: t('transactions.goToDashboard'),
href: '/dashboard',
}}
/>
) : (
<TransactionHistory />
)}
</div>
</div>
);
}

export default function TransactionsPage() {
return <TransactionsContent />;
}
18 changes: 6 additions & 12 deletions src/components/MobileBottomNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
Building2,
Briefcase,
Heart,
User
History,
} from 'lucide-react';
import { cn } from '@/lib/utils';

Expand Down Expand Up @@ -45,29 +45,23 @@ const navItems: NavItem[] = [
icon: Heart,
},
{
id: 'profile',
name: 'Profile',
href: '/dashboard',
icon: User,
id: 'transactions',
name: 'History',
href: '/transactions',
icon: History,
},
];

export const MobileBottomNavigation: React.FC = () => {
const pathname = usePathname();

// Only show on mobile screens
if (typeof window !== 'undefined' && window.innerWidth >= 768) {
return null;
}

return (
<div className="fixed bottom-0 left-0 right-0 z-50 md:hidden">
<div className="bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700">
<nav className="flex items-center justify-around h-16 px-2">
{navItems.map((item) => {
const isActive = pathname === item.href ||
(item.id === 'portfolio' && pathname.startsWith('/dashboard')) ||
(item.id === 'profile' && pathname.startsWith('/dashboard'));
(item.id === 'portfolio' && pathname.startsWith('/dashboard'));

return (
<Link
Expand Down
Loading