@@ -6,15 +6,19 @@ import { prisma } from "@/lib/db";
66import { authOptions } from "@/lib/auth" ;
77import { hasPermission } from "@/lib/permissions" ;
88import { statusLabel , statusColor } from "@/lib/visibility" ;
9+ import { compareProblemSetRecords } from "@/lib/problem-set-order" ;
910import { DeleteSetButton } from "./delete-set-button" ;
1011
1112export const dynamic = "force-dynamic" ;
1213
1314type AdminSetsSearchParams = Promise < {
1415 page ?: string ;
1516 q ?: string ;
17+ status ?: string ;
1618} > ;
1719
20+ type SetStatusFilter = "all" | "PUBLISHED" | "DRAFT" | "ARCHIVED" ;
21+
1822export default async function AdminSetsPage ( {
1923 searchParams,
2024} : {
@@ -27,17 +31,27 @@ export default async function AdminSetsPage({
2731 const params = ( await searchParams ) ?? { } ;
2832 const query = params . q ?. trim ( ) ?? "" ;
2933 const normalizedQuery = query . toLowerCase ( ) ;
34+ const statusFilter : SetStatusFilter =
35+ params . status === "published"
36+ ? "PUBLISHED"
37+ : params . status === "draft"
38+ ? "DRAFT"
39+ : params . status === "archived"
40+ ? "ARCHIVED"
41+ : "all" ;
3042 const currentPage = Math . max ( 1 , Number ( params . page ?? "1" ) || 1 ) ;
3143 const pageSize = 25 ;
3244
3345 const sets = await prisma . problemSet . findMany ( {
34- orderBy : { order : "asc" } ,
46+ orderBy : { createdAt : "asc" } ,
3547 include : {
3648 _count : { select : { problems : true } } ,
3749 } ,
3850 } ) ;
3951
40- const visibleSets = sets . filter ( ( set ) => {
52+ const orderedSets = [ ...sets ] . sort ( compareProblemSetRecords ) ;
53+ const visibleSets = orderedSets . filter ( ( set ) => {
54+ if ( statusFilter !== "all" && set . status !== statusFilter ) return false ;
4155 if ( ! normalizedQuery ) return true ;
4256 return [ set . title , set . slug , String ( set . order ) , ...set . topicTags , set . status ]
4357 . join ( " " )
@@ -48,14 +62,24 @@ export default async function AdminSetsPage({
4862 const safePage = Math . min ( currentPage , totalPages ) ;
4963 const paginatedSets = visibleSets . slice ( ( safePage - 1 ) * pageSize , safePage * pageSize ) ;
5064
51- function setsHref ( page : number ) {
65+ function setsHref ( next : { page ? : number ; status ?: SetStatusFilter } = { } ) {
5266 const urlParams = new URLSearchParams ( ) ;
67+ const nextPage = next . page ?? safePage ;
68+ const nextStatus = next . status ?? statusFilter ;
5369 if ( query ) urlParams . set ( "q" , query ) ;
54- if ( page > 1 ) urlParams . set ( "page" , String ( page ) ) ;
70+ if ( nextStatus !== "all" ) urlParams . set ( "status" , nextStatus . toLowerCase ( ) ) ;
71+ if ( nextPage > 1 ) urlParams . set ( "page" , String ( nextPage ) ) ;
5572 const suffix = urlParams . toString ( ) ;
5673 return suffix ? `/admin/sets?${ suffix } ` : "/admin/sets" ;
5774 }
5875
76+ const statusOptions : Array < { label : string ; value : SetStatusFilter } > = [
77+ { label : "All" , value : "all" } ,
78+ { label : "Published" , value : "PUBLISHED" } ,
79+ { label : "Draft" , value : "DRAFT" } ,
80+ { label : "Archived" , value : "ARCHIVED" } ,
81+ ] ;
82+
5983 return (
6084 < main className = "single-page" >
6185 < div className = "background-layers" aria-hidden = "true" >
@@ -90,6 +114,9 @@ export default async function AdminSetsPage({
90114 name = "q"
91115 placeholder = "Search sets by title, slug, order, tag, or status"
92116 />
117+ { statusFilter !== "all" ? (
118+ < input name = "status" type = "hidden" value = { statusFilter . toLowerCase ( ) } />
119+ ) : null }
93120 < button className = "secondary-action compact" type = "submit" >
94121 Search
95122 </ button >
@@ -100,6 +127,21 @@ export default async function AdminSetsPage({
100127 ) : null }
101128 </ form >
102129
130+ < section className = "admin-set-filter-bar" aria-label = "Problem set status filters" >
131+ < span className = "leaderboard-control-label" > Status</ span >
132+ < div className = "segmented-control" >
133+ { statusOptions . map ( ( option ) => (
134+ < Link
135+ className = { `segmented-button${ statusFilter === option . value ? " active" : "" } ` }
136+ href = { setsHref ( { page : 1 , status : option . value } ) }
137+ key = { option . value }
138+ >
139+ { option . label }
140+ </ Link >
141+ ) ) }
142+ </ div >
143+ </ section >
144+
103145 { visibleSets . length === 0 ? (
104146 < section className = "panel empty-state" >
105147 < FileJson size = { 42 } />
@@ -120,7 +162,9 @@ export default async function AdminSetsPage({
120162 < section className = "panel table-panel" >
121163 < div className = "panel-header" >
122164 < div >
123- < p className = "eyebrow" > All sets</ p >
165+ < p className = "eyebrow" >
166+ { statusFilter === "all" ? "All sets" : `${ statusFilter . toLowerCase ( ) } sets` }
167+ </ p >
124168 < h2 >
125169 { visibleSets . length } problem set{ visibleSets . length !== 1 ? "s" : "" }
126170 </ h2 >
@@ -188,7 +232,7 @@ export default async function AdminSetsPage({
188232 < div className = "pagination-row" >
189233 < Link
190234 className = "secondary-action compact"
191- href = { setsHref ( Math . max ( 1 , safePage - 1 ) ) }
235+ href = { setsHref ( { page : Math . max ( 1 , safePage - 1 ) } ) }
192236 >
193237 Previous
194238 </ Link >
@@ -197,7 +241,7 @@ export default async function AdminSetsPage({
197241 </ span >
198242 < Link
199243 className = "secondary-action compact"
200- href = { setsHref ( Math . min ( totalPages , safePage + 1 ) ) }
244+ href = { setsHref ( { page : Math . min ( totalPages , safePage + 1 ) } ) }
201245 >
202246 Next
203247 </ Link >
0 commit comments