diff --git a/components/campaigns/CampaignSummary.tsx b/components/campaigns/CampaignSummary.tsx index f3c2464d3..4213a7589 100644 --- a/components/campaigns/CampaignSummary.tsx +++ b/components/campaigns/CampaignSummary.tsx @@ -20,6 +20,8 @@ import { import { ScrollArea } from '../ui/scroll-area'; import { formatDate } from '@/lib/utils'; import { Button } from '../ui/button'; +import BackingHistory from './backing-history'; +import { sampleBackers } from '@/lib/data/backing-history-mock'; const CampaignSummary = ({ open, @@ -31,6 +33,7 @@ const CampaignSummary = ({ const [expandedMilestone, setExpandedMilestone] = useState( null ); + const [openHistory, setOpenHistory] = useState(false); // Mock milestone data const milestones = [ @@ -125,237 +128,250 @@ const CampaignSummary = ({ }, ]; return ( - -
-
-
- Campaign Summary -
+ <> + + +
-
-
-

Boundless

- Successful -
-
- #web3 - #crowdfunding -
+
+ Campaign Summary
-
- - - CN - +
-

- Collins Odumeje -

-
-
-

- This campaign successfully reached its funding goal. Contributions - are now being distributed through escrow as milestones are - completed.{' '} - - Click to track milestone progress - -

-
-
-
-
-

Raised

-

- $123,000.00 -

+
+

Boundless

+ Successful +
+
+ #web3 + #crowdfunding +
-
-

Target

-

- $123,000.00 -

+
+ + + CN + +
+

+ Collins Odumeje +

+
+

+ This campaign successfully reached its funding goal. + Contributions are now being distributed through escrow as + milestones are completed.{' '} + + Click to track milestone progress + +

- -
-
- - - 100k - - - - 100k - -
-
-
- - 100 backers +
+
+
+

Raised

+

+ $123,000.00 +

+
+
+

Target

+

+ $123,000.00 +

+
-
-
- - 100 days left + +
+
+ + + 100k + + + + 100k + +
+
+
+ + 100 backers +
+
+
+ + 100 days left +
-
-
-
-

- Campaign Details -

- -

- Boundless is a trustless, decentralized application (dApp) that - empowers changemakers and builders to raise funds transparently - without intermediaries. Campaigns are structured around clearly - defined milestones, with funds held in escrow and released only - upon approval. Grant creators can launch programs with - rule-based logic, and applicants can apply with proposals that - go through public validation. The platform is built on the - Stellar blockchain and powered by Soroban smart contracts to - ensure transparency, security, and autonomy. -

-
-
-
-

Milestones

-
- {milestones.map((milestone, idx) => { - const isExpanded = expandedMilestone === milestone.id; - return ( -
-
- Milestone {idx + 1} -
-
-
toggle(milestone.id)} - > -
- {milestone.title || `Milestone ${idx + 1}`} -
- +
+
+

+ Campaign Details +

+ +

+ Boundless is a trustless, decentralized application (dApp) + that empowers changemakers and builders to raise funds + transparently without intermediaries. Campaigns are structured + around clearly defined milestones, with funds held in escrow + and released only upon approval. Grant creators can launch + programs with rule-based logic, and applicants can apply with + proposals that go through public validation. The platform is + built on the Stellar blockchain and powered by Soroban smart + contracts to ensure transparency, security, and autonomy. +

+
+
+
+

Milestones

+
+ {milestones.map((milestone, idx) => { + const isExpanded = expandedMilestone === milestone.id; + return ( +
+
+ Milestone {idx + 1}
- {isExpanded && ( -
-
- {milestone.description} +
+
toggle(milestone.id)} + > +
+ {milestone.title || `Milestone ${idx + 1}`}
-
-
- - - {formatDate(milestone.deliveryDate)} - + +
+ {isExpanded && ( +
+
+ {milestone.description}
-
- - $ - {calculateFundAmount( - milestone.fundPercentage - ).toLocaleString()}{' '} - ({milestone.fundPercentage || 0}%) - +
+
+ + + {formatDate(milestone.deliveryDate)} + +
+
+ + $ + {calculateFundAmount( + milestone.fundPercentage + ).toLocaleString()}{' '} + ({milestone.fundPercentage || 0}%) + +
-
- )} + )} +
-
- ); - })} -
-
-
-
-

- Backing History -

- + ); + })} +
-
- {backingHistory.map(backer => ( -
+
+

+ Backing History +

+ +
+
+ {backingHistory.map(backer => ( +
+
+
+ + + + {backer.name.charAt(0)} + + + {backer.isVerified && ( +
+ +
+ )} +
+
+
+ {backer.name} +
+
+ + {backer.wallet}
- )} -
-
-
- {backer.name}
-
- - {backer.wallet} +
+
+
+ ${backer.amount.toLocaleString()}
-
-
-
- ${backer.amount.toLocaleString()} +
+
+ {backer.time} +
-
-
{backer.time}
-
-
- ))} + ))} +
+
-
-
- + + ); }; diff --git a/components/campaigns/CampaignTable.tsx b/components/campaigns/CampaignTable.tsx index 3cf47c587..08efed3f9 100644 --- a/components/campaigns/CampaignTable.tsx +++ b/components/campaigns/CampaignTable.tsx @@ -156,6 +156,12 @@ const CampaignRow = ({ > {campaign.status === 'live' && ( <> + handleAction('back-project')} + className='text-white font-medium hover:!text-white text-sm py-2 px-3 rounded-md hover:!bg-[#2B2B2B] hover:shadow-[0_1px_4px_0_rgba(40,45,40,0.04),_0_0_24px_1px_rgba(10,15,10,0.14)] transition-colors duration-200 cursor-pointer' + > + Back Project + handleAction('share')} className='text-white font-medium hover:!text-white text-sm py-2 px-3 rounded-md hover:!bg-[#2B2B2B] hover:shadow-[0_1px_4px_0_rgba(40,45,40,0.04),_0_0_24px_1px_rgba(10,15,10,0.14)] transition-colors duration-200 cursor-pointer' diff --git a/components/campaigns/back-project/index.tsx b/components/campaigns/back-project/index.tsx index c8a3207ba..9d89eee73 100644 --- a/components/campaigns/back-project/index.tsx +++ b/components/campaigns/back-project/index.tsx @@ -4,7 +4,7 @@ import { useState } from 'react'; import { BoundlessButton } from '@/components/buttons'; import { ProjectSubmissionSuccess } from '@/components/project'; import BoundlessSheet from '@/components/sheet/boundless-sheet'; -import { ProjectSubmissionLoading } from '@/components/flows/back-project/project-submission-loading'; +import { ProjectSubmissionLoading } from './project-submission-loading'; import { BackProjectForm } from './back-project-form'; type BackProjectState = 'form' | 'loading' | 'success'; diff --git a/components/campaigns/backing-history/backing-history-table.tsx b/components/campaigns/backing-history/backing-history-table.tsx index bbbf3da68..04c0e4c18 100644 --- a/components/campaigns/backing-history/backing-history-table.tsx +++ b/components/campaigns/backing-history/backing-history-table.tsx @@ -36,67 +36,69 @@ const BackingHistoryTable: React.FC = ({ }; return ( - <> - {/* Results Header */} -
-
Backer
-
Amount
-
Date
-
+
+
+ {/* Results Header */} +
+
Backer
+
Amount
+
Date
+
- {/* Backing List */} -
- {backers.map(backer => ( -
-
-
- - - - {backer.isAnonymous ? ( - - ) : ( - backer.name.charAt(0) - )} - - -
- -
-
-
-
- {backer.name} + {/* Backing List */} +
+ {backers.map(backer => ( +
+
+
+ + + + {backer.isAnonymous ? ( + + ) : ( + backer.name.charAt(0) + )} + + +
+ +
-
- - {backer.walletId} +
+
+ {backer.name} +
+
+ + {backer.walletId} +
+
+ ${backer.amount.toLocaleString()} +
+
+ {formatDate(backer.date)} +
-
- ${backer.amount.toLocaleString()} -
-
- {formatDate(backer.date)} -
+ ))} +
+ + {backers.length === 0 && ( +
+ No backers found matching your criteria
- ))} + )}
- - {backers.length === 0 && ( -
- No backers found matching your criteria -
- )} - +
); }; diff --git a/components/campaigns/backing-history/backing-history.tsx b/components/campaigns/backing-history/backing-history.tsx deleted file mode 100644 index 6f8e85275..000000000 --- a/components/campaigns/backing-history/backing-history.tsx +++ /dev/null @@ -1,506 +0,0 @@ -'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; diff --git a/components/campaigns/backing-history/filter-popover.tsx b/components/campaigns/backing-history/filter-popover.tsx index 686d5db16..eff70158b 100644 --- a/components/campaigns/backing-history/filter-popover.tsx +++ b/components/campaigns/backing-history/filter-popover.tsx @@ -68,8 +68,9 @@ const AdvancedFilterPopover: React.FC = ({
@@ -87,7 +88,7 @@ const AdvancedFilterPopover: React.FC = ({
-
+
= ({ onOpenChange={setShowFromCalendar} > -
+
@@ -121,7 +122,7 @@ const AdvancedFilterPopover: React.FC = ({
-
+
= ({ ? format(dateRange.to, 'MM-dd-yy') : '06-04-25' } - className='bg-muted/20 p-5 border-muted-foreground/20 text-white pr-8 cursor-pointer' + className='bg-[#101010] p-5 border-muted-foreground/20 text-white pr-8 cursor-pointer' readOnly /> @@ -155,12 +156,12 @@ const AdvancedFilterPopover: React.FC = ({
-
+
@@ -168,7 +169,7 @@ const AdvancedFilterPopover: React.FC = ({ variant='outline' size='sm' onClick={() => setQuickDateFilter(7)} - className='bg-muted/20 rounded-3xl p-5 border-muted-foreground/20 text-white hover:bg-muted/30 text-xs' + className='bg-[#101010] rounded-3xl p-3 sm:p-5 border-muted-foreground/20 text-white hover:bg-muted/30 text-xs flex-1 sm:flex-none min-w-0' > Last 7 days @@ -176,7 +177,7 @@ const AdvancedFilterPopover: React.FC = ({ variant='outline' size='sm' onClick={() => setQuickDateFilter(30)} - className='bg-muted/20 rounded-3xl p-5 border-muted-foreground/20 text-white hover:bg-muted/30 text-xs' + className='bg-[#101010] rounded-3xl p-3 sm:p-5 border-muted-foreground/20 text-white hover:bg-muted/30 text-xs flex-1 sm:flex-none min-w-0' > Last month @@ -198,7 +199,7 @@ const AdvancedFilterPopover: React.FC = ({
-
+
@@ -211,7 +212,7 @@ const AdvancedFilterPopover: React.FC = ({ amountRange[1], ]) } - className='bg-muted/20 border-muted-foreground/20 text-white pl-8 p-5' + className='bg-[#101010] border-muted-foreground/20 text-white pl-8 p-5' />
@@ -227,7 +228,7 @@ const AdvancedFilterPopover: React.FC = ({ Number.parseInt(e.target.value) || 0, ]) } - className='bg-muted/20 border-muted-foreground/20 text-white pl-8 p-5' + className='bg-[#101010] border-muted-foreground/20 text-white pl-8 p-5' />
@@ -262,7 +263,7 @@ const AdvancedFilterPopover: React.FC = ({ onClick={() => setIdentityFilter('all')} className={`w-full justify-between bg-transparent p-5 border-none text-white hover:bg-muted/30 ${ identityFilter === 'all' - ? 'bg-muted/40 border-muted-foreground/20' + ? 'bg-[#2b2b2b] border-muted-foreground/20' : '' }`} > @@ -274,7 +275,7 @@ const AdvancedFilterPopover: React.FC = ({ onClick={() => setIdentityFilter('identified')} className={`w-full justify-start p-5 bg-transparent border-none text-white hover:bg-muted/30 ${ identityFilter === 'identified' - ? 'bg-muted/40 border-muted-foreground/20' + ? 'bg-[#2b2b2b] border-muted-foreground/20' : '' }`} > @@ -295,11 +296,11 @@ const AdvancedFilterPopover: React.FC = ({
{/* Action Buttons */} -
+
diff --git a/components/campaigns/backing-history/index.tsx b/components/campaigns/backing-history/index.tsx index f457a3f33..8900a5a94 100644 --- a/components/campaigns/backing-history/index.tsx +++ b/components/campaigns/backing-history/index.tsx @@ -118,45 +118,47 @@ const BackingHistory: React.FC = ({ return (
-
+

Backing History

{/* Search and Controls */} -
-
+
+
setSearchQuery(e.target.value)} - className='pl-10 py-5 focus:outline-none placeholder:font-medium bg-[#1c1c1c] border-muted-foreground/20 text-placeholder placeholder:text-muted-foreground' + className='pl-10 py-5 focus:outline-none placeholder:font-medium bg-[#1c1c1c] border-muted-foreground/20 text-placeholder placeholder:text-muted-foreground w-full' + /> +
+
+ +
- -
diff --git a/components/campaigns/backing-history/sort-filter-popover.tsx b/components/campaigns/backing-history/sort-filter-popover.tsx index ecb0629bc..98f2a7d7c 100644 --- a/components/campaigns/backing-history/sort-filter-popover.tsx +++ b/components/campaigns/backing-history/sort-filter-popover.tsx @@ -48,33 +48,30 @@ const SortFilterPopover: React.FC = ({
@@ -85,18 +82,17 @@ const SortFilterPopover: React.FC = ({ Backer name
@@ -107,32 +103,30 @@ const SortFilterPopover: React.FC = ({
diff --git a/components/flows/back-project/back-project-form.tsx b/components/flows/back-project/back-project-form.tsx deleted file mode 100644 index 21c52022d..000000000 --- a/components/flows/back-project/back-project-form.tsx +++ /dev/null @@ -1,201 +0,0 @@ -'use client'; - -import type React from 'react'; -import { useState } from 'react'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; -import { Checkbox } from '@/components/ui/checkbox'; -import { ArrowLeft, Check, Copy } from 'lucide-react'; -import { BoundlessButton } from '@/components/buttons'; - -interface BackProjectFormProps { - onSubmit: (data: { - amount: string; - currency: string; - token: string; - network: string; - walletAddress: string; - keepAnonymous: boolean; - }) => void; - isLoading?: boolean; -} - -const QUICK_AMOUNTS = [10, 20, 30, 50, 100, 500, 1000]; - -export function BackProjectForm({ - onSubmit, - isLoading = false, -}: BackProjectFormProps) { - const [amount, setAmount] = useState(''); - const [currency] = useState('USDT'); - const [token, setToken] = useState(''); - const [network, setNetwork] = useState('Stella / Soroban'); - const [walletAddress] = useState('GDS3...GB7'); - const [keepAnonymous, setKeepAnonymous] = useState(false); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - onSubmit({ - amount, - currency, - token, - network, - walletAddress, - keepAnonymous, - }); - }; - - const handleQuickAmount = (quickAmount: number) => { - setAmount(quickAmount.toString()); - }; - - const handleCopyAddress = async (e: React.MouseEvent) => { - e.preventDefault(); - try { - await navigator.clipboard.writeText(walletAddress); - // Could add toast notification here instead of state - } catch { - // Fallback for browsers that don't support clipboard API - const textArea = document.createElement('textarea'); - // Silent fallback for older browsers - textArea.value = walletAddress; - document.body.appendChild(textArea); - textArea.select(); - try { - document.execCommand('copy'); - } catch { - // Copy failed, but no need to log in production - } - document.body.removeChild(textArea); - } - }; - - const isFormValid = amount && currency && token && walletAddress; - - return ( -
-
- -

Back Project

-
-
-
-

- Funds will be held in escrow and released only upon milestone - approvals. -

-
- -
- -
- {currency} - setAmount(e.target.value)} - type='number' - className='w-full bg-transparent font-normal text-base text-placeholder focus:outline-none' - placeholder='1000' - disabled={isLoading} - /> -
-

min. amount: $10

- -
- {QUICK_AMOUNTS.map(quickAmount => ( - - ))} -
-
- -
- - -
- -
- -
- setNetwork(e.target.value)} - type='text' - className='w-full bg-transparent font-normal text-base text-placeholder focus:outline-none' - disabled={isLoading} - /> -
-
- -
- - - - {walletAddress} - - -
- -
- setKeepAnonymous(checked as boolean)} - disabled={isLoading} - className='border-stepper-border data-[state=checked]:bg-primary data-[state=checked]:border-primary' - /> - -
- - - Confirm Contribution - -
-
- ); -} diff --git a/components/flows/back-project/index.tsx b/components/flows/back-project/index.tsx deleted file mode 100644 index c8a3207ba..000000000 --- a/components/flows/back-project/index.tsx +++ /dev/null @@ -1,112 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { BoundlessButton } from '@/components/buttons'; -import { ProjectSubmissionSuccess } from '@/components/project'; -import BoundlessSheet from '@/components/sheet/boundless-sheet'; -import { ProjectSubmissionLoading } from '@/components/flows/back-project/project-submission-loading'; -import { BackProjectForm } from './back-project-form'; - -type BackProjectState = 'form' | 'loading' | 'success'; - -interface BackProjectData { - amount: string; - currency: string; - token: string; - network: string; - walletAddress: string; - keepAnonymous: boolean; -} - -const BackProject = () => { - const [isSheetOpen, setIsSheetOpen] = useState(false); - const [backProjectState, setBackProjectState] = - useState('form'); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const handleBackProject = (data: BackProjectData) => { - setBackProjectState('loading'); - // TODO: Send data to actual API endpoint when backend is ready - - // Simulate API call - data will be used when API is implemented - setTimeout(() => { - setBackProjectState('success'); - }, 2000); - }; - - // const handleContinue = () => { - // setIsSheetOpen(false) - // setBackProjectState("form") - // } - - // const handleViewHistory = () => { - // // Navigate to history page or open history modal - // setIsSheetOpen(false) - // // TODO: Implement backing history modal or navigation - // } - - // const handleBack = () => { - // if (backProjectState === "success") { - // setBackProjectState("form") - // } - // } - - const renderSheetContent = () => { - if (backProjectState === 'success') { - return ( -
-
- {/* */} -
- -
- ); - } - - return ( -
- - - {backProjectState === 'loading' && ( -
- -
- )} -
- ); - }; - - return ( -
- - {renderSheetContent()} - - - setIsSheetOpen(true)}> - Back Project - -
- ); -}; - -export default BackProject; diff --git a/components/flows/back-project/project-submission-loading.tsx b/components/flows/back-project/project-submission-loading.tsx deleted file mode 100644 index 4dc27ea2d..000000000 --- a/components/flows/back-project/project-submission-loading.tsx +++ /dev/null @@ -1,15 +0,0 @@ -export function ProjectSubmissionLoading() { - return ( -
-
- {/* Outer spinning ring */} -
- {/* Inner spinning arc */} -
-
-

- Processing your contribution... -

-
- ); -} diff --git a/components/flows/backing-history/backing-history.tsx b/components/flows/backing-history/backing-history.tsx deleted file mode 100644 index 6f8e85275..000000000 --- a/components/flows/backing-history/backing-history.tsx +++ /dev/null @@ -1,506 +0,0 @@ -'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;