diff --git a/src/components/home/CreatorSpotlight.tsx b/src/components/home/CreatorSpotlight.tsx new file mode 100644 index 0000000..6837a10 --- /dev/null +++ b/src/components/home/CreatorSpotlight.tsx @@ -0,0 +1,228 @@ +import { useQuery } from '@tanstack/react-query'; +import { Users, Star, ArrowRight } from 'lucide-react'; +import { useEffect, useRef } from 'react'; +import { Link } from 'react-router'; +import { courseService, type Course } from '@/services/course.service'; +import { queryKeys } from '@/lib/queryKeys'; +import { STROOPS_PER_XLM } from '@/constants/stellar'; +import { formatHolderCount } from '@/utils/numberFormat.utils'; +import { normalizeCreatorDisplayName } from '@/utils/creatorDisplayName.utils'; +import { cn } from '@/lib/utils'; +import CreatorInitialsAvatar from '@/components/common/CreatorInitialsAvatar'; + +/* ------------------------------------------------------------------ */ +/* Skeleton */ +/* ------------------------------------------------------------------ */ + +const skeletonBlockClass = + 'rounded-lg bg-white/12 skeleton-shimmer motion-reduce:bg-white/18 motion-reduce:ring-1 motion-reduce:ring-white/15'; + +function SpotlightSkeletonCard() { + return ( +
+ Loading spotlight creator + + {/* Rank badge placeholder */} +
+ + {/* Avatar */} +
+ + {/* Name */} +
+ + {/* Stats row */} +
+
+
+
+
+ ); +} + +function SpotlightSkeletonGrid() { + return ( +
+ + + +
+ ); +} + +/* ------------------------------------------------------------------ */ +/* Card */ +/* ------------------------------------------------------------------ */ + +interface SpotlightCreatorCardProps { + creator: Course; + rank: number; +} + +function SpotlightCreatorCard({ creator, rank }: SpotlightCreatorCardProps) { + const name = normalizeCreatorDisplayName(creator.title) || 'Unnamed creator'; + const keyPrice = + creator.priceStroops != null + ? (creator.priceStroops / STROOPS_PER_XLM).toFixed(2) + : creator.price.toFixed(2); + const holderCount = creator.creatorShareSupply ?? 0; + + return ( + + {/* Rank badge */} +
+ {rank === 1 ? ( + + + Top + + ) : ( + + #{rank} + + )} +
+ + {/* Avatar */} +
+ +
+ + {/* Name */} +

+ {name} +

+ + {/* Stats row */} +
+ {/* Key price */} +
+ + {keyPrice} + + XLM +
+ + {/* Holder count */} +
+ + + {formatHolderCount(holderCount)} + +
+
+ + {/* CTA */} +
+ View profile + +
+ + ); +} + +/* ------------------------------------------------------------------ */ +/* Section */ +/* ------------------------------------------------------------------ */ + +export default function CreatorSpotlight() { + const headingRef = useRef(null); + const gridRef = useRef(null); + + const { data: creators, isLoading } = useQuery({ + queryKey: queryKeys.creators.list({ sort: 'supply-desc', limit: 3 }), + queryFn: () => courseService.getCourses({ sort: 'supply-desc', limit: 3 }), + }); + + useEffect(() => { + const observer = new IntersectionObserver( + entries => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('is-visible'); + observer.unobserve(entry.target); + } + }); + }, + { threshold: 0.1 } + ); + + if (headingRef.current) observer.observe(headingRef.current); + if (gridRef.current) observer.observe(gridRef.current); + + return () => observer.disconnect(); + }, []); + + // Hide section when no creators returned (AC: Section hidden when no creators) + if (!isLoading && (!creators || creators.length < 1)) { + return null; + } + + return ( +
+
+ {/* Header */} +
+
+ + + Creator Spotlight + +
+ +
+

+ + Most collected creators + +
+ + discover who has the biggest following. + +

+ + View all + + +
+
+ + {/* Grid or Skeleton */} + {isLoading ? ( +
+ +
+ ) : ( +
+ {creators?.map((creator, index) => ( + + ))} +
+ )} +
+
+ ); +} diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index 8c8806d..cd62d61 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -2,6 +2,7 @@ import FAQ from '../components/home/FAQ'; import Footer from '../components/home/Footer'; import Header from '../components/home/Header'; import Hero from '../components/home/Hero'; +import CreatorSpotlight from '../components/home/CreatorSpotlight'; import TrendingCreators from '../components/home/TrendingCreators'; import { useNavigationTiming } from '../hooks/useNavigationTiming'; @@ -13,6 +14,7 @@ export default function HomePage() {
+