diff --git a/src/components/common/ClearedFiltersEmptyState.tsx b/src/components/common/ClearedFiltersEmptyState.tsx new file mode 100644 index 0000000..c88cd0e --- /dev/null +++ b/src/components/common/ClearedFiltersEmptyState.tsx @@ -0,0 +1,63 @@ +import { cn } from '@/lib/utils'; +import { Button } from '@/components/ui/button'; +import { SlidersHorizontal, ArrowRight } from 'lucide-react'; + +interface ClearedFiltersEmptyStateProps { + /** + * Called when the user clicks "Browse all creators". + * Typically resets sort to 'featured' and clears any residual state. + */ + onBrowseAll?: () => void; + className?: string; +} + +/** + * Shown when all filters have been cleared but the creator list is still + * empty. Distinct from the search-no-results empty state — this state means + * the marketplace itself has no listings, not that a specific query failed. + */ +const ClearedFiltersEmptyState: React.FC = ({ + onBrowseAll, + className, +}) => ( +
+
+
+ + +
+ +

+ Nothing here yet +

+

+ Your filters are cleared, but no creators are available right now. + Check back soon or try browsing all categories. +

+ + {onBrowseAll && ( + + )} +
+); + +export default ClearedFiltersEmptyState; diff --git a/src/components/common/CreatorCard.tsx b/src/components/common/CreatorCard.tsx index 562e48a..fc5941b 100644 --- a/src/components/common/CreatorCard.tsx +++ b/src/components/common/CreatorCard.tsx @@ -2,7 +2,16 @@ import { useRef, useState } from 'react'; import { useAccount } from 'wagmi'; import type { Course } from '@/services/course.service'; import { cn } from '@/lib/utils'; -import { ShoppingCart, Link as LinkIcon, TrendingUp } from 'lucide-react'; +import { ShoppingCart, Link as LinkIcon, TrendingUp, MoreVertical, Copy, Share2, ExternalLink } from 'lucide-react'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import RecentActivityBadge from '@/components/common/RecentActivityBadge'; import toast from 'react-hot-toast'; import showToast from '@/utils/toast.util'; import { formatCompactNumber, formatNumber } from '@/utils/numberFormat.utils'; @@ -105,6 +114,28 @@ const CreatorCard: React.FC = ({ }, 1500); }; + const isRecentlyActive = (creator.volume24h ?? 0) > 0; + + const handleCopyLink = () => { + const url = `${window.location.origin}/creator/${creator.id}`; + navigator.clipboard + .writeText(url) + .then(() => toast.success('Profile link copied')) + .catch(() => toast.error('Could not copy link')); + }; + + const handleShare = () => { + const url = `${window.location.origin}/creator/${creator.id}`; + if (navigator.share) { + navigator.share({ title: creator.title, url }).catch(() => {}); + } else { + navigator.clipboard + .writeText(url) + .then(() => toast.success('Link copied to clipboard')) + .catch(() => toast.error('Could not share')); + } + }; + const handleBuy = () => { if (!isConnected) { toast.error('Please connect your wallet to purchase keys', { @@ -134,6 +165,46 @@ const CreatorCard: React.FC = ({ className )} > +
+ + + + + + {creator.title} + + + + + + + {}} + className="cursor-pointer gap-2 text-white/70 focus:bg-white/10 focus:text-white" + > + + + +
= ({ /> + {isRecentlyActive && }

{displayInstructorHandle} diff --git a/src/components/common/CreatorListPagination.tsx b/src/components/common/CreatorListPagination.tsx new file mode 100644 index 0000000..a6e4bbe --- /dev/null +++ b/src/components/common/CreatorListPagination.tsx @@ -0,0 +1,78 @@ +import { cn } from '@/lib/utils'; +import { Button } from '@/components/ui/button'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; + +interface CreatorListPaginationProps { + /** 0-indexed current page. */ + page: number; + totalPages: number; + onPageChange: (page: number) => void; + className?: string; +} + +const CreatorListPagination: React.FC = ({ + page, + totalPages, + onPageChange, + className, +}) => { + const displayPage = page + 1; + + return ( +

+ ); +}; + +export default CreatorListPagination; diff --git a/src/components/common/RecentActivityBadge.tsx b/src/components/common/RecentActivityBadge.tsx new file mode 100644 index 0000000..f488dd0 --- /dev/null +++ b/src/components/common/RecentActivityBadge.tsx @@ -0,0 +1,34 @@ +import { cn } from '@/lib/utils'; + +interface RecentActivityBadgeProps { + className?: string; +} + +/** + * Visible indicator for creator cards with recent trading activity + * (volume24h > 0). Renders a pulsing green dot with an accessible label + * so screen readers understand its meaning. + */ +const RecentActivityBadge: React.FC = ({ + className, +}) => ( + + {/* Pulsing dot — decorative, announced via the sr-only sibling */} +
))} -
- - - Page {safePage + 1} of {totalPages} - - -
+ {safePage >= totalPages - 1 && (

) : (

- - {!hasInvalidSearchInput && ( - + ) : ( + <> + + {!hasInvalidSearchInput && ( + + )} + )}
)}