diff --git a/backend/frontend/app/dashboard/page.tsx b/backend/frontend/app/dashboard/page.tsx index 77780d0b..b46a55dc 100644 --- a/backend/frontend/app/dashboard/page.tsx +++ b/backend/frontend/app/dashboard/page.tsx @@ -1,5 +1,6 @@ 'use client'; +import { useState, useEffect } from 'react'; import { useDashboardData } from '@/lib/hooks/useDashboardData'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { DollarSign, Clock, Folder, CheckCircle2, TrendingUp } from 'lucide-react'; @@ -7,14 +8,62 @@ import { FadeIn } from '@/components/ui/fade-in'; import { DashboardStatsSkeleton } from '@/components/ui/loading-skeletons'; export default function DashboardPage() { - const { stats, recentActivity, loading } = useDashboardData(); + const { stats, recentActivity, loading, refetch } = useDashboardData(); + const [isRefreshing, setIsRefreshing] = useState(false); + const [lastUpdated, setLastUpdated] = useState(null); + + const handleRefresh = async () => { + setIsRefreshing(true); + try { + await refetch?.(); + setLastUpdated(new Date()); + } finally { + setIsRefreshing(false); + } + }; + + useEffect(() => { + handleRefresh(); + const interval = setInterval(handleRefresh, 60000); + return () => clearInterval(interval); + }, []); if (loading) { return (
-
-

Dashboard

-

Welcome back! Here's your overview.

+
+
+

Dashboard

+

Welcome back! Here's your overview.

+
+
+ + {lastUpdated && ( +

+ Last updated: {lastUpdated.toLocaleTimeString()} +

+ )} +
{/* Simplified Loading for Recent Activity */} @@ -36,9 +85,39 @@ export default function DashboardPage() { return (
-
-

Dashboard

-

Welcome back! Here's your overview.

+
+
+

Dashboard

+

Welcome back! Here's your overview.

+
+
+ + {lastUpdated && ( +

+ Last updated: {lastUpdated.toLocaleTimeString()} +

+ )} +
{/* Stats Grid */} diff --git a/frontend/app/dashboard/projects/page.tsx b/frontend/app/dashboard/projects/page.tsx index ad62b99d..63994885 100644 --- a/frontend/app/dashboard/projects/page.tsx +++ b/frontend/app/dashboard/projects/page.tsx @@ -19,6 +19,8 @@ const FILTER_PRESETS_KEY = 'agenticpay-project-filter-presets'; const STATUS_OPTIONS = ['active', 'completed', 'cancelled'] as const; type StatusOption = (typeof STATUS_OPTIONS)[number]; +type SortField = 'date' | 'amount' | 'status'; +type SortDirection = 'asc' | 'desc'; type FilterPreset = { name: string; @@ -44,6 +46,19 @@ function getStatusColor(status: string): string { } } +const SortIcon = ({ + field, + sortField, + sortDirection, +}: { + field: SortField; + sortField: SortField; + sortDirection: SortDirection; +}) => { + if (field !== sortField) return ; + return {sortDirection === 'asc' ? '↑' : '↓'}; +}; + export default function ProjectsPage() { const router = useRouter(); const { isConnected } = useAccount(); @@ -58,6 +73,8 @@ export default function ProjectsPage() { const [maxAmount, setMaxAmount] = useState(''); const [presetName, setPresetName] = useState(''); const [presets, setPresets] = useState>({}); + const [sortField, setSortField] = useState('date'); + const [sortDirection, setSortDirection] = useState('desc'); useEffect(() => { const stored = window.localStorage.getItem(FILTER_PRESETS_KEY); @@ -93,6 +110,20 @@ export default function ProjectsPage() { }); }, [projects, statusFilter, startDate, endDate, minAmount, maxAmount]); + const sortedProjects = useMemo(() => { + return [...filteredProjects].sort((a, b) => { + let comparison = 0; + if (sortField === 'date') { + comparison = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); + } else if (sortField === 'amount') { + comparison = Number(a.totalAmount) - Number(b.totalAmount); + } else if (sortField === 'status') { + comparison = a.status.localeCompare(b.status); + } + return sortDirection === 'asc' ? comparison : -comparison; + }); + }, [filteredProjects, sortField, sortDirection]); + const savePreset = () => { if (!presetName.trim()) return; setPresets((current) => ({ @@ -133,6 +164,15 @@ export default function ProjectsPage() { }); }; + const handleSort = (field: SortField) => { + if (field === sortField) { + setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc')); + } else { + setSortField(field); + setSortDirection('asc'); + } + }; + if (loading) { return (
@@ -308,6 +348,28 @@ export default function ProjectsPage() { {/* Project cards */}
+ {/* Sort controls */} +
+ + + +
+ {filteredProjects.length === 0 && ( @@ -329,7 +391,7 @@ export default function ProjectsPage() { )} - {filteredProjects.map((project, index) => { + {sortedProjects.map((project, index) => { const completedMilestones = project.milestones.filter( (m) => m.status === 'completed', ).length;