@@ -5,23 +5,37 @@ import { Button } from '@/components/ui/button';
55import { ActivityCard , mergeActivity , type ActivityItem } from '@/components/ActivityCard' ;
66import { HelpWantedCard } from '@/components/HelpWantedCard' ;
77import { HeroSlideshow } from '@/components/HeroSlideshow' ;
8+ import { StageBadge } from '@/components/StageBadge' ;
89import { UpcomingEventsStrip } from '@/components/UpcomingEventsStrip' ;
910import { useAuth } from '@/hooks/useAuth' ;
1011import { api } from '@/lib/api' ;
1112import { cn } from '@/lib/utils' ;
1213
13- function FeaturedTile ( { title, summary, slug, imageUrl } : { title : string ; summary : string | null ; slug : string ; imageUrl ?: string | null } ) {
14+ function FeaturedTile ( {
15+ title,
16+ summary,
17+ slug,
18+ stage,
19+ imageUrl,
20+ } : {
21+ title : string ;
22+ summary : string | null ;
23+ slug : string ;
24+ stage : string ;
25+ imageUrl ?: string | null ;
26+ } ) {
1427 return (
1528 < Link
1629 to = { `/projects/${ slug } ` }
1730 className = "group flex flex-col rounded-lg overflow-hidden border border-border bg-card hover:shadow-lg transition-shadow"
1831 >
19- { imageUrl ? (
20- < img src = { imageUrl } alt = "" className = "w-full h-44 object-cover" loading = "lazy" />
21- ) : (
22- < div className = "w-full h-44 bg-gradient-to-br from-primary/10 to-primary/30" />
23- ) }
32+ { /* No placeholder panel without an image: six blank panels read as a
33+ dead page (specs/screens/home.md). */ }
34+ { imageUrl && < img src = { imageUrl } alt = "" className = "w-full h-44 object-cover" loading = "lazy" /> }
2435 < div className = "p-4" >
36+ < div className = "mb-2" >
37+ < StageBadge stage = { stage } />
38+ </ div >
2539 < h3 className = "font-semibold text-foreground group-hover:text-primary transition-colors" >
2640 { title }
2741 </ h3 >
@@ -33,6 +47,8 @@ function FeaturedTile({ title, summary, slug, imageUrl }: { title: string; summa
3347 ) ;
3448}
3549
50+ const FEATURED_TILES = 6 ;
51+
3652const ACTIVITY_FILTERS = [ 'all' , 'updates' , 'buzz' ] as const ;
3753type ActivityFilter = ( typeof ACTIVITY_FILTERS ) [ number ] ;
3854
@@ -41,10 +57,24 @@ export function Home() {
4157 const [ activityFilter , setActivityFilter ] = useState < ActivityFilter > ( 'all' ) ;
4258
4359 const featuredQ = useQuery ( {
44- queryKey : [ 'projects' , { featured : true , perPage : 8 } ] ,
45- queryFn : ( ) => api . projects . list ( { featured : true , perPage : 8 } ) ,
60+ queryKey : [ 'projects' , { featured : true , perPage : FEATURED_TILES } ] ,
61+ queryFn : ( ) => api . projects . list ( { featured : true , perPage : FEATURED_TILES } ) ,
62+ } ) ;
63+
64+ // Tops up the curated picks so the section is never empty for want of
65+ // curation (specs/screens/home.md → Featured projects).
66+ const activeQ = useQuery ( {
67+ queryKey : [ 'projects' , { sort : '-activity' , perPage : FEATURED_TILES * 2 } ] ,
68+ queryFn : ( ) => api . projects . list ( { sort : '-activity' , perPage : FEATURED_TILES * 2 } ) ,
4669 } ) ;
4770
71+ const tiles = useMemo ( ( ) => {
72+ const seen = new Set < string > ( ) ;
73+ return [ ...( featuredQ . data ?. data ?? [ ] ) , ...( activeQ . data ?. data ?? [ ] ) ]
74+ . filter ( ( p ) => ! seen . has ( p . slug ) && seen . add ( p . slug ) )
75+ . slice ( 0 , FEATURED_TILES ) ;
76+ } , [ featuredQ . data , activeQ . data ] ) ;
77+
4878 const totalCountQ = useQuery ( {
4979 queryKey : [ 'projects' , { perPage : 1 , count : true } ] ,
5080 queryFn : ( ) => api . projects . list ( { perPage : 1 } ) ,
@@ -104,16 +134,17 @@ export function Home() {
104134 { /* Featured projects */ }
105135 < section className = "container mx-auto px-4 py-12" >
106136 < h2 className = "text-2xl font-bold mb-6" > Join a Project</ h2 >
107- { featuredQ . isLoading ? (
108- < p className = "text-muted-foreground" > Loading featured projects…</ p >
137+ { featuredQ . isLoading || activeQ . isLoading ? (
138+ < p className = "text-muted-foreground" > Loading projects…</ p >
109139 ) : (
110140 < div className = "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4" >
111- { ( featuredQ . data ?. data ?? [ ] ) . map ( ( p ) => (
141+ { tiles . map ( ( p ) => (
112142 < FeaturedTile
113143 key = { p . slug }
114144 title = { p . title }
115145 summary = { p . summary || p . overviewExcerpt }
116146 slug = { p . slug }
147+ stage = { p . stage }
117148 imageUrl = { p . featuredImageUrl ?? null }
118149 />
119150 ) ) }
@@ -130,10 +161,11 @@ export function Home() {
130161 < section className = "bg-muted/30 border-y border-border" >
131162 < div className = "container mx-auto px-4 py-12" >
132163 < div className = "grid grid-cols-1 md:grid-cols-3 gap-4" >
133- < Link to = "/sponsor" className = "block rounded-lg border border-border bg-card p-6 hover:shadow-md transition-shadow" >
134- < h3 className = "text-lg font-semibold mb-1" > Sponsor</ h3 >
135- < p className = "text-sm text-muted-foreground" > Sponsor an event</ p >
136- </ Link >
164+ { /* A plain anchor: /chat is a server redirect (specs/screens/chat.md). */ }
165+ < a href = "/chat" className = "block rounded-lg border border-border bg-card p-6 hover:shadow-md transition-shadow" >
166+ < h3 className = "text-lg font-semibold mb-1" > Join us on Slack</ h3 >
167+ < p className = "text-sm text-muted-foreground" > Say hello in #general and meet the community</ p >
168+ </ a >
137169 < Link
138170 to = { person ? '/projects/create' : '/login?return=/projects/create' }
139171 className = "block rounded-lg border border-border bg-card p-6 hover:shadow-md transition-shadow"
0 commit comments