diff --git a/components/flows/backing-history/backing-history.tsx b/components/flows/backing-history/backing-history.tsx new file mode 100644 index 000000000..6f8e85275 --- /dev/null +++ b/components/flows/backing-history/backing-history.tsx @@ -0,0 +1,506 @@ +'use client'; + +import type React from 'react'; +import { useState, useMemo } from 'react'; +import { + Search, + Filter, + ArrowUpDown, + Calendar, + DollarSign, + User, + Wallet, + Check, + CheckIcon, +} from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { Slider } from '@/components/ui/slider'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { format } from 'date-fns'; +import BoundlessSheet from '@/components/sheet/boundless-sheet'; + +interface Backer { + id: string; + name: string; + avatar?: string; + amount: number; + date: Date; + walletId: string; + isAnonymous: boolean; +} + +interface BackingHistoryProps { + open: boolean; + setOpen: (open: boolean) => void; + backers: Backer[]; +} + +type SortOption = 'newest' | 'oldest' | 'alphabetical' | 'highest' | 'lowest'; +type IdentityFilter = 'all' | 'identified' | 'anonymous'; + +const BackingHistory: React.FC = ({ + open, + setOpen, + backers, +}) => { + const [searchQuery, setSearchQuery] = useState(''); + const [sortBy, setSortBy] = useState('newest'); + const [amountRange, setAmountRange] = useState([0, 10000]); + const [dateRange, setDateRange] = useState<{ from?: Date; to?: Date }>({}); + const [identityFilter, setIdentityFilter] = useState('all'); + const [showFilters, setShowFilters] = useState(false); + const [showSortPopover, setShowSortPopover] = useState(false); + + const setQuickDateFilter = (days: number) => { + const today = new Date(); + const pastDate = new Date(today.getTime() - days * 24 * 60 * 60 * 1000); + setDateRange({ from: pastDate, to: today }); + }; + + const resetFilters = () => { + setSearchQuery(''); + setSortBy('newest'); + setAmountRange([0, 10000]); + setDateRange({}); + setIdentityFilter('all'); + }; + + const resetDateRange = () => { + setDateRange({}); + }; + + const resetAmountRange = () => { + setAmountRange([10, 1000]); + }; + + const resetIdentityFilter = () => { + setIdentityFilter('all'); + }; + + const applyFilters = () => { + setShowSortPopover(false); + }; + + const filteredAndSortedBackers = useMemo(() => { + const filtered = backers.filter(backer => { + const matchesSearch = + backer.name.toLowerCase().includes(searchQuery.toLowerCase()) || + backer.walletId.toLowerCase().includes(searchQuery.toLowerCase()); + + const matchesAmount = + backer.amount >= amountRange[0] && backer.amount <= amountRange[1]; + + const matchesDate = + !dateRange.from || + !dateRange.to || + (backer.date >= dateRange.from && backer.date <= dateRange.to); + + const matchesIdentity = + identityFilter === 'all' || + (identityFilter === 'anonymous' && backer.isAnonymous) || + (identityFilter === 'identified' && !backer.isAnonymous); + + return matchesSearch && matchesAmount && matchesDate && matchesIdentity; + }); + + filtered.sort((a, b) => { + switch (sortBy) { + case 'newest': + return b.date.getTime() - a.date.getTime(); + case 'oldest': + return a.date.getTime() - b.date.getTime(); + case 'alphabetical': + return a.name.localeCompare(b.name); + case 'highest': + return b.amount - a.amount; + case 'lowest': + return a.amount - b.amount; + default: + return 0; + } + }); + + return filtered; + }, [backers, searchQuery, sortBy, amountRange, dateRange, identityFilter]); + + const formatDate = (date: Date) => { + const now = new Date(); + const diffInDays = Math.floor( + (now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24) + ); + + if (diffInDays === 0) return 'Today'; + if (diffInDays === 1) return '1d'; + if (diffInDays < 7) return `${diffInDays}d`; + if (diffInDays < 30) return `${Math.floor(diffInDays / 7)}w`; + return format(date, 'MMM dd, yyyy'); + }; + + return ( + +
+
+ {/* Search and Controls */} +
+
+ + setSearchQuery(e.target.value)} + className='pl-10 py-5 placeholder:font-medium bg-muted/20 border-muted-foreground/20 text-white placeholder:text-muted-foreground' + /> +
+ + + + + + +
+ {/* Date Range Section */} +
+
+

+ Date range +

+ +
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ + + +
+
+
+ + {/* Amount Range Section */} +
+
+

+ Amount range +

+ +
+
+
+
+ +
+ + + setAmountRange([ + Number.parseInt(e.target.value) || 0, + amountRange[1], + ]) + } + className='bg-muted/20 border-muted-foreground/20 text-white pl-8' + /> +
+
+
+ +
+ + + setAmountRange([ + amountRange[0], + Number.parseInt(e.target.value) || 0, + ]) + } + className='bg-muted/20 border-muted-foreground/20 text-white pl-8' + /> +
+
+
+ +
+
+ + {/* Identity Type Section */} +
+
+

+ Identity Type +

+ +
+
+ + + +
+
+ + {/* Action Buttons */} +
+ + +
+
+
+
+
+ + {/* Filters Panel */} + {showFilters && ( +
+ {/* Sort Options */} +
+
+ + +
+ +
+
+ )} + + {/* Results Header */} +
+
Backer
+
Amount
+
Date
+
+ + {/* Backing List */} +
+ {filteredAndSortedBackers.map(backer => ( +
+
+
+ + + + {backer.isAnonymous ? ( + + ) : ( + backer.name.charAt(0) + )} + + +
+ +
+
+
+
{backer.name}
+
+ + {backer.walletId} +
+
+
+
+ ${backer.amount.toLocaleString()} +
+
+ {formatDate(backer.date)} +
+
+ ))} +
+ + {filteredAndSortedBackers.length === 0 && ( +
+ No backers found matching your criteria +
+ )} +
+
+
+ ); +}; + +export default BackingHistory;