@@ -15,6 +15,7 @@ import type {
1515} from "@/lib/types" ;
1616import TagsList from "@/components/ui/TagsList" ;
1717import InitialAvatar from "@/components/ui/InitialAvatar" ;
18+ import { formatRelativeDate } from "@/lib/utils" ;
1819import AppCard from "@/components/cards/AppCard" ;
1920import MechanismCard from "@/components/cards/MechanismCard" ;
2021import ResearchCard from "@/components/cards/ResearchCard" ;
@@ -68,22 +69,31 @@ const BASE_HREFS: Record<ContentType, string> = {
6869
6970// ─── Helpers ──────────────────────────────────────────────────────────────────
7071
72+ function sortKey ( item : BaseContent ) : string {
73+ return ( item as Campaign ) . startDate ?? item . lastUpdated ;
74+ }
75+
76+ // Active = campaign that hasn't ended yet (no endDate or endDate in the future)
77+ function isActive ( item : BaseContent ) : boolean {
78+ const endDate = ( item as Campaign ) . endDate ;
79+ if ( endDate === undefined ) return false ; // not a campaign
80+ return ! endDate || new Date ( endDate ) > new Date ( ) ;
81+ }
82+
7183function sortItems ( items : BaseContent [ ] , sort : SortOption ) : BaseContent [ ] {
7284 const sorted = [ ...items ] ;
7385 if ( sort === "newest" )
74- return sorted . sort ( ( a , b ) => b . lastUpdated . localeCompare ( a . lastUpdated ) ) ;
86+ return sorted . sort ( ( a , b ) => {
87+ const aActive = isActive ( a ) ;
88+ const bActive = isActive ( b ) ;
89+ if ( aActive !== bActive ) return aActive ? - 1 : 1 ;
90+ return sortKey ( b ) . localeCompare ( sortKey ( a ) ) ;
91+ } ) ;
7592 if ( sort === "oldest" )
76- return sorted . sort ( ( a , b ) => a . lastUpdated . localeCompare ( b . lastUpdated ) ) ;
93+ return sorted . sort ( ( a , b ) => sortKey ( a ) . localeCompare ( sortKey ( b ) ) ) ;
7794 return sorted . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
7895}
7996
80- function formatDate ( dateStr : string ) {
81- return new Date ( dateStr ) . toLocaleDateString ( "en-US" , {
82- year : "numeric" ,
83- month : "short" ,
84- } ) ;
85- }
86-
8797// ─── Sub-components ───────────────────────────────────────────────────────────
8898
8999function GridCard ( { item, type } : { item : BaseContent ; type : ContentType } ) {
@@ -147,10 +157,12 @@ function ListRow({
147157 item,
148158 href,
149159 preferLogo,
160+ showDate,
150161} : {
151162 item : BaseContent ;
152163 href : string ;
153164 preferLogo : boolean ;
165+ showDate : boolean ;
154166} ) {
155167 return (
156168 < Link href = { href } >
@@ -163,9 +175,11 @@ function ListRow({
163175 < h3 className = "font-semibold text-gray-25 line-clamp-3 text-base sm:text-2xl" >
164176 { item . name }
165177 </ h3 >
166- < span className = "text-xs text-gray-500 shrink-0 tabular-nums" >
167- { formatDate ( item . lastUpdated ) }
168- </ span >
178+ { showDate && (
179+ < span className = "text-xs text-gray-500 shrink-0 tabular-nums" >
180+ { formatRelativeDate ( ( item as Campaign ) . startDate ?? item . lastUpdated ) }
181+ </ span >
182+ ) }
169183 </ div >
170184 < p className = "text-sm text-gray-300 font-serif mt-0.5 line-clamp-2 sm:line-clamp-4" >
171185 { item . shortDescription }
@@ -304,6 +318,7 @@ export function CategoryContent({
304318 const sorted = useMemo ( ( ) => sortItems ( filtered , sort ) , [ filtered , sort ] ) ;
305319 const baseHref = BASE_HREFS [ type ] ;
306320 const preferLogo = type === "app" ;
321+ const showDate = type !== "app" ;
307322
308323 return (
309324 < div >
@@ -353,6 +368,7 @@ export function CategoryContent({
353368 item = { item }
354369 href = { `${ baseHref } /${ item . slug } ` }
355370 preferLogo = { preferLogo }
371+ showDate = { showDate }
356372 />
357373 ) ) }
358374 </ div >
0 commit comments