11"use client" ;
22
3- import { useMemo , useState } from "react" ;
3+ import { useMemo , useState , useCallback } from "react" ;
44import { useRouter , usePathname } from "next/navigation" ;
55import Link from "next/link" ;
66import { toast } from "sonner" ;
@@ -38,6 +38,7 @@ import {
3838 Square ,
3939 ArrowRight ,
4040 Trash2 ,
41+ User ,
4142} from "lucide-react" ;
4243import { formatDistanceToNow } from "date-fns" ;
4344import {
@@ -48,10 +49,14 @@ import {
4849 useUpdateSessionDisplayName ,
4950} from "@/services/queries/use-sessions" ;
5051import { useProjectAccess } from "@/services/queries/use-project-access" ;
52+ import { useCurrentUser } from "@/services/queries/use-auth" ;
5153import { useVersion } from "@/services/queries/use-version" ;
54+ import { useLocalStorage } from "@/hooks/use-local-storage" ;
5255import { cn } from "@/lib/utils" ;
5356import type { AgenticSession } from "@/types/api" ;
5457
58+ type StatusFilter = "all" | "running" | "completed" | "failed" ;
59+
5560type SessionsSidebarProps = {
5661 projectName : string ;
5762 currentSessionName : string ;
@@ -102,20 +107,59 @@ export function SessionsSidebar({
102107 const continueMutation = useContinueSession ( ) ;
103108 const updateDisplayNameMutation = useUpdateSessionDisplayName ( ) ;
104109
110+ const { data : currentUser } = useCurrentUser ( ) ;
111+
105112 const [ editingSession , setEditingSession ] = useState < { name : string ; displayName : string } | null > ( null ) ;
106113 const [ deletingSessionName , setDeletingSessionName ] = useState < string | null > ( null ) ;
114+ const [ mineOnly , setMineOnly ] = useLocalStorage ( `acp:sidebar:mine:${ projectName } ` , false ) ;
115+ const [ statusFilter , setStatusFilter ] = useLocalStorage < StatusFilter > ( `acp:sidebar:status:${ projectName } ` , "all" ) ;
116+
117+ const toggleMineOnly = useCallback ( ( ) => setMineOnly ( ( prev : boolean ) => ! prev ) , [ setMineOnly ] ) ;
118+ const toggleStatusFilter = useCallback (
119+ ( filter : StatusFilter ) => setStatusFilter ( ( prev : StatusFilter ) => prev === filter ? "all" : filter ) ,
120+ [ setStatusFilter ] ,
121+ ) ;
107122
108123 const sessions = useMemo ( ( ) => {
109124 const items = data ?. items ?? [ ] ;
110125 return [ ...items ] . sort ( ( a , b ) => getActivityTime ( b ) - getActivityTime ( a ) ) ;
111126 } , [ data ?. items ] ) ;
112127
128+ const filteredSessions = useMemo ( ( ) => {
129+ let result = sessions ;
130+
131+ if ( mineOnly && currentUser ?. userId ) {
132+ result = result . filter (
133+ ( s ) => s . spec . userContext ?. userId === currentUser . userId ,
134+ ) ;
135+ }
136+
137+ if ( statusFilter !== "all" ) {
138+ result = result . filter ( ( s ) => {
139+ const phase = s . status ?. phase ;
140+ switch ( statusFilter ) {
141+ case "running" :
142+ return phase === "Running" || phase === "Pending" || phase === "Creating" ;
143+ case "completed" :
144+ return phase === "Completed" || phase === "Stopped" ;
145+ case "failed" :
146+ return phase === "Failed" ;
147+ default :
148+ return true ;
149+ }
150+ } ) ;
151+ }
152+
153+ return result ;
154+ } , [ sessions , mineOnly , currentUser ?. userId , statusFilter ] ) ;
155+
113156 const visibleSessions = useMemo ( ( ) => {
114- if ( showAll ) return sessions ;
115- return sessions . slice ( 0 , INITIAL_RECENTS_COUNT ) ;
116- } , [ sessions , showAll ] ) ;
157+ if ( showAll ) return filteredSessions ;
158+ return filteredSessions . slice ( 0 , INITIAL_RECENTS_COUNT ) ;
159+ } , [ filteredSessions , showAll ] ) ;
117160
118- const hasMore = sessions . length > INITIAL_RECENTS_COUNT && ! showAll ;
161+ const hasMore = filteredSessions . length > INITIAL_RECENTS_COUNT && ! showAll ;
162+ const isFiltered = mineOnly || statusFilter !== "all" ;
119163
120164 const navItems : NavItem [ ] = useMemo (
121165 ( ) => [
@@ -307,15 +351,49 @@ export function SessionsSidebar({
307351 < span className = "text-xs font-medium text-muted-foreground uppercase tracking-wider" >
308352 Recents
309353 </ span >
310- < button
311- type = "button"
354+ < Button
355+ variant = "ghost"
356+ size = "sm"
312357 onClick = { ( ) => refetch ( ) }
313358 disabled = { isFetching }
314- className = "text-muted-foreground/60 hover:text-muted-foreground transition-colors disabled:opacity-50 "
359+ className = "h-5 w-5 p-0 text-muted-foreground/60 hover:text-muted-foreground"
315360 title = { dataUpdatedAt ? `Last updated ${ formatDistanceToNow ( new Date ( dataUpdatedAt ) , { addSuffix : true } ) } ` : "Refresh" }
316361 >
317362 < RefreshCw className = "h-3 w-3" />
318- </ button >
363+ </ Button >
364+ </ div >
365+
366+ { /* Filter chips */ }
367+ < div className = "flex items-center gap-1 px-3 pb-1 flex-wrap" data-testid = "sidebar-filters" >
368+ < Button
369+ variant = { mineOnly ? "default" : "secondary" }
370+ size = "sm"
371+ onClick = { toggleMineOnly }
372+ className = { cn (
373+ "h-auto rounded-full px-2 py-0.5 text-[0.6875rem] font-medium gap-1" ,
374+ ! mineOnly && "text-muted-foreground" ,
375+ ) }
376+ title = "Show only my sessions"
377+ data-testid = "filter-mine"
378+ >
379+ < User className = "h-3 w-3" />
380+ Mine
381+ </ Button >
382+ { ( [ "running" , "completed" , "failed" ] as const ) . map ( ( filter ) => (
383+ < Button
384+ key = { filter }
385+ variant = { statusFilter === filter ? "default" : "secondary" }
386+ size = "sm"
387+ onClick = { ( ) => toggleStatusFilter ( filter ) }
388+ className = { cn (
389+ "h-auto rounded-full px-2 py-0.5 text-[0.6875rem] font-medium capitalize" ,
390+ statusFilter !== filter && "text-muted-foreground" ,
391+ ) }
392+ data-testid = { `filter-${ filter } ` }
393+ >
394+ { filter }
395+ </ Button >
396+ ) ) }
319397 </ div >
320398
321399 < div className = "flex-1 overflow-y-auto" >
@@ -325,9 +403,9 @@ export function SessionsSidebar({
325403 < Skeleton key = { i } className = "h-10 w-full rounded-md" />
326404 ) ) }
327405 </ div >
328- ) : sessions . length === 0 ? (
406+ ) : filteredSessions . length === 0 ? (
329407 < div className = "p-4 text-center text-sm text-muted-foreground" >
330- No sessions yet
408+ { isFiltered ? " No matching sessions" : "No sessions yet" }
331409 </ div >
332410 ) : (
333411 < div className = "space-y-0.5 p-1" >
0 commit comments