22import { useEffect , useState , useCallback , useRef } from "react" ;
33import { useParams } from "next/navigation" ;
44import { copyText } from "@/lib/clipboard" ;
5+ import { filterSignupsByStatus , type WaitlistStatus } from "@/lib/waitlist-filter" ;
56import MoshpitTlds from "@/components/MoshpitTlds" ;
67
78// Tab values double as URL slugs: /dashboard/<tab> (the default "page" tab lives at
@@ -757,6 +758,7 @@ function WaitlistPanel({ onError }: { onError: (m: string) => void }) {
757758 const [ domains , setDomains ] = useState < any [ ] | undefined > ( undefined ) ;
758759 const [ active , setActive ] = useState < string | null > ( null ) ;
759760 const [ signups , setSignups ] = useState < any [ ] | null > ( null ) ;
761+ const [ status , setStatus ] = useState < WaitlistStatus > ( "all" ) ;
760762
761763 const load = async ( dn : string ) => {
762764 setActive ( dn ) ; setSignups ( null ) ;
@@ -781,6 +783,15 @@ function WaitlistPanel({ onError }: { onError: (m: string) => void }) {
781783 return < section className = "card2" > < h2 > Waitlist</ h2 > < p className = "sub" > No parked domains yet — claim one on the “Domains” tab and its waitlist shows up here.</ p > </ section > ;
782784 }
783785
786+ const filtered = signups ? filterSignupsByStatus ( signups , status ) : null ;
787+ const verifiedCount = signups ?. filter ( ( signup ) => signup . verified ) . length ?? 0 ;
788+ const pendingCount = signups ? signups . length - verifiedCount : 0 ;
789+ const filters : { value : WaitlistStatus ; label : string ; count : number } [ ] = [
790+ { value : "all" , label : "All" , count : signups ?. length ?? 0 } ,
791+ { value : "verified" , label : "Confirmed" , count : verifiedCount } ,
792+ { value : "pending" , label : "Pending" , count : pendingCount } ,
793+ ] ;
794+
784795 return (
785796 < section className = "card2" >
786797 < h2 > Waitlist</ h2 >
@@ -795,14 +806,33 @@ function WaitlistPanel({ onError }: { onError: (m: string) => void }) {
795806 { active && (
796807 < >
797808 < div className = "row" style = { { justifyContent : "space-between" , alignItems : "center" } } >
798- < h3 className = "ed-h" > { active } — { signups ? signups . length : "…" } signups</ h3 >
799- < a className = "btn2 ghost" href = { `/api/waitlist/manage?dn=${ encodeURIComponent ( active ) } &format=csv` } > Export CSV</ a >
809+ < h3 className = "ed-h" > { active } — { filtered ? filtered . length : "…" } signups</ h3 >
810+ < a className = "btn2 ghost" href = { `/api/waitlist/manage?dn=${ encodeURIComponent ( active ) } &format=csv&status=${ status } ` } >
811+ { status === "all" ? "Export CSV" : "Export filtered CSV" }
812+ </ a >
813+ </ div >
814+ < div className = "tabs" role = "group" aria-label = "Filter waitlist signups" >
815+ { filters . map ( ( filter ) => (
816+ < button
817+ key = { filter . value }
818+ type = "button"
819+ className = { `tab${ status === filter . value ? " on" : "" } ` }
820+ aria-pressed = { status === filter . value }
821+ onClick = { ( ) => setStatus ( filter . value ) }
822+ >
823+ { filter . label } < span className = "muted" > ({ filter . count } )</ span >
824+ </ button >
825+ ) ) }
800826 </ div >
801827 < ul className = "list" >
802828 { signups === null && < li className = "muted" > Loading…</ li > }
803- { signups && signups . length === 0 && < li className = "muted" > No signups yet — share your page.</ li > }
804- { signups && signups . map ( ( s , i ) => (
805- < li key = { i } >
829+ { filtered && filtered . length === 0 && (
830+ < li className = "muted" >
831+ { signups ?. length ? `No ${ status === "verified" ? "confirmed" : status } signups.` : "No signups yet — share your page." }
832+ </ li >
833+ ) }
834+ { filtered && filtered . map ( ( s ) => (
835+ < li key = { s . email } >
806836 < span > { s . email } </ span >
807837 < span className = "muted" > { s . verified ? "✓ confirmed" : "pending" } { s . ref ? ` · ref:${ s . ref } ` : "" } </ span >
808838 </ li >
0 commit comments