Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/components/common/CreatorListGroupSeparator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { cn } from '@/lib/utils';

interface CreatorListGroupSeparatorProps {
label: string;
className?: string;
}

/**
* Visual separator with label between creator list groups (e.g., pinned vs unpinned).
* Spans the full grid width and includes an accessible label.
*/
const CreatorListGroupSeparator: React.FC<CreatorListGroupSeparatorProps> = ({
label,
className,
}) => {
return (
<div
role="separator"
aria-label={label}
className={cn(
'col-span-full flex items-center gap-3 py-3',
className
)}
>
<div
className="h-px flex-1 bg-gradient-to-r from-transparent via-white/[0.08] to-transparent"
aria-hidden="true"
/>
<span className="shrink-0 text-[0.65rem] font-semibold uppercase tracking-[0.2em] text-white/55 md:text-xs">
{label}
</span>
<div
className="h-px flex-1 bg-gradient-to-r from-transparent via-white/[0.08] to-transparent"
aria-hidden="true"
/>
</div>
);
};

export default CreatorListGroupSeparator;
76 changes: 55 additions & 21 deletions src/pages/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { CREATOR_LIST_SORT_LAYOUT_TRANSITION } from '@/utils/creatorListSortTran
import { AlertCircle, ChevronDown, RefreshCw } from 'lucide-react';
import ClearedFiltersEmptyState from '@/components/common/ClearedFiltersEmptyState';
import CreatorListPagination from '@/components/common/CreatorListPagination';
import CreatorListGroupSeparator from '@/components/common/CreatorListGroupSeparator';
import MarketplaceSidebar from '@/components/common/MarketplaceSidebar';

const FEATURED_CREATOR_FACTS = [
Expand Down Expand Up @@ -101,6 +102,7 @@ const DEMO_CREATORS: Course[] = [
category: 'Art',
level: 'BEGINNER',
isVerified: true,
isPinned: true,
thumbnail:
'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=400&fit=crop',
},
Expand All @@ -115,6 +117,7 @@ const DEMO_CREATORS: Course[] = [
category: 'Tech',
level: 'ADVANCED',
isVerified: true,
isPinned: true,
thumbnail:
'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=400&h=400&fit=crop',
},
Expand Down Expand Up @@ -682,27 +685,58 @@ function LandingPage() {
)}
<LayoutGroup>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
{pagedCreators.map((creator, index) => (
// #300: staggered entry animation; the
// helper no-ops on prefers-reduced-motion.
// #355: layout transition when sort order changes.
<motion.div
key={creator.id}
layout={!prefersReducedMotion}
transition={
CREATOR_LIST_SORT_LAYOUT_TRANSITION
}
className={CREATOR_CARD_ENTRY_CLASS}
style={creatorCardEntryStyle(index, {
prefersReducedMotion,
})}
>
<CreatorCard
creator={creator}
isPriceRefreshing={isPriceRefreshing}
/>
</motion.div>
))}
{/* Render pinned creators first */}
{pagedCreators
.filter(creator => creator.isPinned)
.map((creator, index) => (
// #300: staggered entry animation; the
// helper no-ops on prefers-reduced-motion.
// #355: layout transition when sort order changes.
<motion.div
key={creator.id}
layout={!prefersReducedMotion}
transition={
CREATOR_LIST_SORT_LAYOUT_TRANSITION
}
className={CREATOR_CARD_ENTRY_CLASS}
style={creatorCardEntryStyle(index, {
prefersReducedMotion,
})}
>
<CreatorCard
creator={creator}
isPriceRefreshing={isPriceRefreshing}
/>
</motion.div>
))}

{/* Separator between pinned and unpinned */}
{pagedCreators.some(creator => creator.isPinned) &&
pagedCreators.some(creator => !creator.isPinned) && (
<CreatorListGroupSeparator label="Other creators" />
)}

{/* Render unpinned creators */}
{pagedCreators
.filter(creator => !creator.isPinned)
.map((creator, index) => (
<motion.div
key={creator.id}
layout={!prefersReducedMotion}
transition={
CREATOR_LIST_SORT_LAYOUT_TRANSITION
}
className={CREATOR_CARD_ENTRY_CLASS}
style={creatorCardEntryStyle(index, {
prefersReducedMotion,
})}
>
<CreatorCard
creator={creator}
isPriceRefreshing={isPriceRefreshing}
/>
</motion.div>
))}
</div>
</LayoutGroup>
<CreatorListPagination
Expand Down
2 changes: 2 additions & 0 deletions src/services/course.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface Course {
volume24h?: number;
change24h?: number;
joinedAt?: string;
/** Whether this creator is pinned in the marketplace list. */
isPinned?: boolean;
}

export interface GetCoursesParams {
Expand Down
Loading