@@ -10,8 +10,6 @@ import {
1010 MoreHorizontal ,
1111 Trash2 ,
1212 FolderKanban ,
13- Plus ,
14- FileText ,
1513 Loader2 ,
1614 Search ,
1715 Calendar ,
@@ -67,6 +65,8 @@ import { AxiosResponse } from "axios";
6765import { useScheduleExecutions } from "@/hooks/useScheduleExecutions" ;
6866import { useSchedules } from "@/hooks/useSchedules" ;
6967import { ScheduleSidebarItem } from "@/components/sidebar/ScheduleSidebarItem" ;
68+ import { ProjectTreeGroup } from "@/components/sidebar/ProjectTreeGroup" ;
69+ import { useProjectThreads } from "@/hooks/useProjectThreads" ;
7070
7171interface AssistantItemProps {
7272 agent : Agent ;
@@ -361,197 +361,7 @@ function ThreadItem({ thread, projects }: ThreadItemProps) {
361361 ) ;
362362}
363363
364- interface ProjectItemProps {
365- project : Project ;
366- onAddSource : ( project : Project ) => void ;
367- }
368-
369- function ProjectItem ( { project, onAddSource } : ProjectItemProps ) {
370- const { selectedProject, selectProject, handleDeleteProject } =
371- useProjectContext ( ) ;
372- const { setMetadata } = useChatContext ( ) ;
373- const { isMobile, setOpenMobile } = useSidebar ( ) ;
374- const navigate = useNavigate ( ) ;
375-
376- const isSelected = selectedProject ?. id === project . id ;
377- const sourceCount = project . sources ?. length || 0 ;
378-
379- const handleProjectClick = ( ) => {
380- selectProject ( project ) ;
381- setMetadata ( ( prev : any ) => ( {
382- ...prev ,
383- project_id : project . id ,
384- } ) ) ;
385- if ( isMobile ) {
386- setOpenMobile ( false ) ;
387- }
388- // Navigate to project page
389- navigate ( `/p/${ project . id } ` ) ;
390- } ;
391-
392- const handleDeleteClick = async ( ) => {
393- if ( window . confirm ( "Are you sure you want to delete this project?" ) ) {
394- const deleted = await handleDeleteProject ( project . id ! ) ;
395- if ( deleted ) {
396- setMetadata ( ( prev : any ) => {
397- const { project_id : _project_id , ...rest } = prev ;
398- return rest ;
399- } ) ;
400- }
401- }
402- } ;
403-
404- const relativeTime = project . updated_at
405- ? formatDistanceToNow ( new Date ( project . updated_at ) , { addSuffix : true } )
406- : "" ;
407-
408- return (
409- < SidebarMenuItem className = "mb-1 group/project relative" >
410- < SidebarMenuButton
411- asChild
412- isActive = { isSelected }
413- className = { `h-auto px-3 py-3 rounded-lg border transition-all ${
414- isSelected
415- ? "bg-sidebar-accent border-sidebar-accent shadow-sm"
416- : "bg-transparent border-sidebar-border hover:bg-sidebar-accent/50 hover:border-sidebar-accent/50"
417- } `}
418- >
419- < button
420- onClick = { handleProjectClick }
421- className = "flex items-start gap-2.5 w-full"
422- >
423- < div className = "flex flex-col min-w-0 flex-1 gap-1.5" >
424- < div className = "flex items-start justify-between gap-2 w-full" >
425- < span
426- className = { `text-sm leading-tight line-clamp-2 ${
427- isSelected
428- ? "font-semibold text-sidebar-accent-foreground"
429- : "font-medium text-sidebar-foreground"
430- } `}
431- >
432- { project . name }
433- </ span >
434- { relativeTime && (
435- < span className = "text-[10px] text-sidebar-foreground/40 shrink-0 font-normal mt-0.5 whitespace-nowrap" >
436- { relativeTime }
437- </ span >
438- ) }
439- </ div >
440- { project . description && (
441- < span className = "text-xs text-sidebar-foreground/60 truncate" >
442- { project . description }
443- </ span >
444- ) }
445- < div className = "flex items-center gap-2.5 text-[11px] text-sidebar-foreground/50" >
446- < div className = "flex items-center gap-1" >
447- < FileText className = "w-3 h-3" />
448- < span >
449- { sourceCount } source{ sourceCount !== 1 ? "s" : "" }
450- </ span >
451- </ div >
452- </ div >
453- </ div >
454- </ button >
455- </ SidebarMenuButton >
456- < DropdownMenu >
457- < DropdownMenuTrigger asChild >
458- < Button
459- variant = "ghost"
460- size = "icon"
461- className = "absolute right-2 bottom-2 opacity-0 group-hover/project:opacity-100 transition-opacity h-6 w-6"
462- onClick = { ( e ) => e . stopPropagation ( ) }
463- >
464- < MoreHorizontal className = "h-3.5 w-3.5 text-sidebar-foreground/60" />
465- </ Button >
466- </ DropdownMenuTrigger >
467- < DropdownMenuContent align = "end" className = "w-48" >
468- < DropdownMenuItem
469- onClick = { ( ) => onAddSource ( project ) }
470- className = "cursor-pointer"
471- >
472- < Plus className = "mr-2 h-4 w-4" />
473- Add Source
474- </ DropdownMenuItem >
475- < DropdownMenuItem
476- onClick = { handleDeleteClick }
477- className = "text-red-300 focus:text-red-400 hover:text-red-300 cursor-pointer"
478- >
479- < Trash2 className = "mr-2 h-4 w-4" />
480- Delete
481- </ DropdownMenuItem >
482- </ DropdownMenuContent >
483- </ DropdownMenu >
484- </ SidebarMenuItem >
485- ) ;
486- }
487-
488- interface ProjectsCollapsibleGroupProps {
489- projects : Project [ ] ;
490- onCreateProject : ( ) => void ;
491- onAddSource : ( project : Project ) => void ;
492- }
493-
494- function ProjectsCollapsibleGroup ( {
495- projects,
496- onCreateProject,
497- onAddSource,
498- } : ProjectsCollapsibleGroupProps ) {
499- return (
500- < Collapsible
501- key = "projects"
502- title = { `Projects (${ projects . length } items)` }
503- defaultOpen = { false }
504- className = "group/collapsible"
505- data-tour = "projects-section"
506- >
507- < SidebarGroup className = "border-b border-sidebar-border" >
508- < SidebarGroupLabel
509- asChild
510- className = { `
511- group/label text-sidebar-foreground hover:bg-sidebar-accent
512- hover:text-sidebar-accent-foreground text-sm
513- ` }
514- >
515- < CollapsibleTrigger >
516- < FolderKanban className = "w-4 h-4 mr-2" />
517- Projects
518- < ChevronRight className = "ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90" />
519- </ CollapsibleTrigger >
520- </ SidebarGroupLabel >
521- < CollapsibleContent >
522- < SidebarGroupContent className = "px-1 pt-2" >
523- < div className = "px-2 pb-2" >
524- < Button
525- variant = "outline"
526- size = "sm"
527- className = "w-full justify-start gap-2"
528- onClick = { onCreateProject }
529- >
530- < Plus className = "h-4 w-4" />
531- Create Project
532- </ Button >
533- </ div >
534- < SidebarMenu className = "gap-0" >
535- { projects . length > 0 ? (
536- projects . map ( ( project ) => (
537- < ProjectItem
538- key = { project . id }
539- project = { project }
540- onAddSource = { onAddSource }
541- />
542- ) )
543- ) : (
544- < div className = "px-3 py-4 text-center text-sm text-sidebar-foreground/50" >
545- No projects yet
546- </ div >
547- ) }
548- </ SidebarMenu >
549- </ SidebarGroupContent >
550- </ CollapsibleContent >
551- </ SidebarGroup >
552- </ Collapsible >
553- ) ;
554- }
364+ // ProjectItem and ProjectsCollapsibleGroup replaced by ProjectTreeGroup
555365
556366interface CollapsibleGroupProps {
557367 title : string ;
@@ -617,7 +427,7 @@ function CollapsibleGroup({
617427 < Collapsible
618428 key = { title }
619429 title = { `${ title } (${ items . length } items)` }
620- defaultOpen = { type === "threads" }
430+ defaultOpen = { false }
621431 className = "group/collapsible"
622432 { ...( type === "threads" ? { "data-tour" : "threads-section" } : { } ) }
623433 >
@@ -813,6 +623,15 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
813623 } = useChatContext ( ) ;
814624 const { projects, useEffectGetProjects } = useProjectContext ( ) ;
815625 const onLogoLinkClick = useLinkClick ( "/" ) ;
626+ const {
627+ projectThreadsMap,
628+ fetchProjectThreads,
629+ loadMoreProjectThreads,
630+ toggleProjectExpanded,
631+ isProjectExpanded,
632+ addThreadToProject,
633+ removeThreadFromProject,
634+ } = useProjectThreads ( ) ;
816635 // Modal state
817636 const [ isCreateProjectModalOpen , setIsCreateProjectModalOpen ] =
818637 useState ( false ) ;
@@ -901,10 +720,17 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
901720 </ SidebarGroupLabel >
902721 </ SidebarGroup >
903722
904- < ProjectsCollapsibleGroup
723+ < ProjectTreeGroup
905724 projects = { projects }
906725 onCreateProject = { ( ) => setIsCreateProjectModalOpen ( true ) }
907726 onAddSource = { handleAddSource }
727+ projectThreadsMap = { projectThreadsMap }
728+ fetchProjectThreads = { fetchProjectThreads }
729+ loadMoreProjectThreads = { loadMoreProjectThreads }
730+ toggleProjectExpanded = { toggleProjectExpanded }
731+ isProjectExpanded = { isProjectExpanded }
732+ addThreadToProject = { addThreadToProject }
733+ removeThreadFromProject = { removeThreadFromProject }
908734 />
909735 < SchedulesCollapsibleGroup />
910736 < CollapsibleGroup
0 commit comments