@@ -70,8 +70,13 @@ export function DashboardClient({ initialData }: DashboardClientProps) {
7070 const [ preBadgeSortCol , setPreBadgeSortCol ] = useState < SortColumn | null > ( null ) ;
7171 const [ groups , setGroups ] = useState < BillingGroupWithMembers [ ] > ( [ ] ) ;
7272 const [ selectedGroup , setSelectedGroup ] = useState ( "all" ) ;
73+ const [ exhaustionFilter , setExhaustionFilter ] = useState < {
74+ label : string ;
75+ emails : Set < string > ;
76+ } | null > ( null ) ;
7377
7478 const groupParam = searchParams . get ( "group" ) ;
79+ const exhaustionParam = searchParams . get ( "exhaustion" ) ;
7580 useEffect ( ( ) => {
7681 fetch ( "/api/groups" )
7782 . then ( ( r ) => r . json ( ) )
@@ -82,6 +87,27 @@ export function DashboardClient({ initialData }: DashboardClientProps) {
8287 }
8388 } )
8489 . catch ( ( ) => { } ) ;
90+ if ( exhaustionParam ) {
91+ const [ minStr , maxStr ] = exhaustionParam . split ( "-" ) ;
92+ const min = parseInt ( minStr ?? "0" , 10 ) ;
93+ const max = parseInt ( maxStr ?? "999" , 10 ) ;
94+ fetch ( "/api/analytics" )
95+ . then ( ( r ) => r . json ( ) )
96+ . then (
97+ ( analytics : {
98+ planExhaustion ?: { users : Array < { email : string ; days_to_exhaust : number } > } ;
99+ } ) => {
100+ const matching = ( analytics . planExhaustion ?. users ?? [ ] ) . filter (
101+ ( u ) => u . days_to_exhaust >= min && u . days_to_exhaust <= max ,
102+ ) ;
103+ setExhaustionFilter ( {
104+ label : `Plan exhausted day ${ min } –${ max } ` ,
105+ emails : new Set ( matching . map ( ( u ) => u . email ) ) ,
106+ } ) ;
107+ } ,
108+ )
109+ . catch ( ( ) => { } ) ;
110+ }
85111 const saved = localStorage . getItem ( "dashboard-days" ) ;
86112 if ( saved ) {
87113 const parsed = parseInt ( saved , 10 ) ;
@@ -170,6 +196,9 @@ export function DashboardClient({ initialData }: DashboardClientProps) {
170196 if ( groupEmailSet ) {
171197 users = users . filter ( ( u ) => groupEmailSet . has ( u . email ) ) ;
172198 }
199+ if ( exhaustionFilter ) {
200+ users = users . filter ( ( u ) => exhaustionFilter . emails . has ( u . email ) ) ;
201+ }
173202 if ( search . trim ( ) ) {
174203 const q = search . toLowerCase ( ) ;
175204 users = users . filter (
@@ -210,7 +239,7 @@ export function DashboardClient({ initialData }: DashboardClientProps) {
210239 }
211240 } ) ;
212241 return sorted ;
213- } , [ stats . rankedUsers , search , sortCol , sortAsc , groupEmailSet , badgeFilter ] ) ;
242+ } , [ stats . rankedUsers , search , sortCol , sortAsc , groupEmailSet , badgeFilter , exhaustionFilter ] ) ;
214243
215244 const searchedUser = useMemo ( ( ) => {
216245 if ( ! search . trim ( ) ) return null ;
@@ -236,7 +265,11 @@ export function DashboardClient({ initialData }: DashboardClientProps) {
236265 }
237266
238267 const timeLabel = formatTimeLabel ( days ) ;
239- const isSearching = search . trim ( ) . length > 0 || selectedGroup !== "all" || badgeFilter !== null ;
268+ const isSearching =
269+ search . trim ( ) . length > 0 ||
270+ selectedGroup !== "all" ||
271+ badgeFilter !== null ||
272+ exhaustionFilter !== null ;
240273 const totalLines = stats . dailyTeamActivity . reduce ( ( s , d ) => s + d . total_lines_added , 0 ) ;
241274 const effectiveDays = Math . min ( days , stats . cycleDays ) ;
242275 const cycleStartDate = new Date ( stats . cycleStart ) ;
@@ -361,6 +394,20 @@ export function DashboardClient({ initialData }: DashboardClientProps) {
361394 ) ) }
362395 </ div >
363396 { loading && < span className = "text-[11px] text-zinc-500 animate-pulse" > Updating...</ span > }
397+ { exhaustionFilter && (
398+ < span className = "inline-flex items-center gap-1.5 bg-orange-600/20 text-orange-300 rounded-md px-2 py-1 text-[11px] font-medium" >
399+ { exhaustionFilter . label } ({ exhaustionFilter . emails . size } )
400+ < button
401+ onClick = { ( ) => {
402+ setExhaustionFilter ( null ) ;
403+ window . history . replaceState ( { } , "" , "/" ) ;
404+ } }
405+ className = "hover:text-orange-100 cursor-pointer"
406+ >
407+ ✕
408+ </ button >
409+ </ span >
410+ ) }
364411 < div className = "ml-auto text-[11px] text-zinc-600" >
365412 { isSearching ? `${ filteredUsers . length } / ` : "" }
366413 { stats . totalMembers } members
0 commit comments