@@ -2,12 +2,10 @@ import { useState, useEffect, useRef, useCallback } from "react";
22import { useTranslation } from "react-i18next" ;
33import { ExternalLink , Database , X , Server } from "lucide-react" ;
44
5- import { StaticScrollArea } from "@/components/ui/scrollArea" ;
65import { ImageItem , ChatRightPanelProps , SearchResult } from "@/types/chat" ;
7- import { API_ENDPOINTS } from "@/services/api" ;
86import { formatDate , formatUrl } from "@/lib/utils" ;
97import { convertImageUrlToApiUrl , extractObjectNameFromUrl , storageService } from "@/services/storageService" ;
10- import { message , Button , Tabs } from "antd" ;
8+ import { message , Button } from "antd" ;
119import log from "@/lib/logger" ;
1210
1311
@@ -26,6 +24,7 @@ export function ChatRightPanel({
2624 const [ processedImages , setProcessedImages ] = useState < string [ ] > ( [ ] ) ;
2725 const [ viewingImage , setViewingImage ] = useState < string | null > ( null ) ;
2826 const [ imageData , setImageData ] = useState < Record < string , ImageItem > > ( { } ) ;
27+ const [ activeTab , setActiveTab ] = useState < string > ( "sources" ) ;
2928
3029 // Reference to prevent duplicate loading
3130 const loadingImages = useRef < Set < string > > ( new Set ( ) ) ;
@@ -532,131 +531,122 @@ export function ChatRightPanel({
532531 ) }
533532 </ div >
534533
535- < Tabs
536- defaultActiveKey = "sources"
537- className = "flex-1 flex flex-col overflow-hidden"
538- style = { { maxWidth : "400px" } }
539- items = { [
540- {
541- key : "sources" ,
542- label : (
543- < span className = "flex items-center justify-center px-3 py-1.5 text-sm font-medium" >
544- { t ( "chatRightPanel.sources" ) }
545- { searchResults . length > 0 && (
546- < span className = "ml-1 bg-gray-200 inline-flex items-center justify-center rounded px-1 text-xs font-medium min-w-[20px] h-[18px]" >
547- { searchResults . length }
548- </ span >
534+ < div className = "flex-1 flex flex-col" style = { { maxWidth : "400px" , height : "100%" } } >
535+ { /* Tab Headers */ }
536+ < div className = "flex border-b bg-gray-50" >
537+ < Button
538+ type = { activeTab === "sources" ? "primary" : "text" }
539+ className = { `flex-1 px-3 py-2 text-sm font-medium transition-colors rounded-none border-none ${
540+ activeTab === "sources"
541+ ? "bg-white text-gray-900 border-b-2 border-blue-500"
542+ : "text-gray-500 hover:text-gray-700 hover:bg-gray-100"
543+ } `}
544+ onClick = { ( ) => setActiveTab ( "sources" ) }
545+ >
546+ < span className = "flex items-center justify-center" >
547+ { t ( "chatRightPanel.sources" ) }
548+ { searchResults . length > 0 && (
549+ < span className = "ml-1 bg-gray-200 inline-flex items-center justify-center rounded px-1 text-xs font-medium min-w-[20px] h-[18px]" >
550+ { searchResults . length }
551+ </ span >
552+ ) }
553+ </ span >
554+ </ Button >
555+ < Button
556+ type = { activeTab === "images" ? "primary" : "text" }
557+ className = { `flex-1 px-3 py-2 text-sm font-medium transition-colors rounded-none border-none ${
558+ activeTab === "images"
559+ ? "bg-white text-gray-900 border-b-2 border-blue-500"
560+ : "text-gray-500 hover:text-gray-700 hover:bg-gray-100"
561+ } `}
562+ onClick = { ( ) => setActiveTab ( "images" ) }
563+ >
564+ < span className = "flex items-center justify-center" >
565+ { t ( "chatRightPanel.images" ) }
566+ { processedImages . length > 0 && (
567+ < span className = "ml-1 bg-gray-200 inline-flex items-center justify-center rounded px-1 text-xs font-medium min-w-[20px] h-[18px]" >
568+ { processedImages . length }
569+ </ span >
570+ ) }
571+ </ span >
572+ </ Button >
573+ </ div >
574+
575+ { /* Tab Content */ }
576+ < div className = "flex-1 overflow-y-auto" >
577+ { activeTab === "sources" && (
578+ < div className = "p-4" style = { { maxWidth : "400px" } } >
579+ < div className = "space-y-2" style = { { maxWidth : "100%" } } >
580+ { searchResults . length > 0 ? (
581+ < >
582+ < div className = "space-y-3" style = { { maxWidth : "100%" } } >
583+ { searchResults . map ( ( result , index ) => (
584+ < SearchResultItem
585+ key = { `result-${ index } ` }
586+ result = { result }
587+ />
588+ ) ) }
589+ </ div >
590+ </ >
591+ ) : (
592+ < div className = "text-center text-gray-500 py-4 text-base" >
593+ { t ( "chatRightPanel.noSearchResults" ) }
594+ </ div >
549595 ) }
550- </ span >
551- ) ,
552- children : (
553- < StaticScrollArea
554- className = "flex-1"
555- style = { { maxWidth : "400px" , overflow : "hidden" } }
556- >
557- < div
558- className = "p-4"
559- style = { { maxWidth : "400px" , overflow : "hidden" } }
560- >
561- < div
562- className = "space-y-2"
563- style = { { maxWidth : "100%" , overflow : "hidden" } }
564- >
565- { searchResults . length > 0 ? (
566- < >
596+ </ div >
597+ </ div >
598+ ) }
599+
600+ { activeTab === "images" && (
601+ < div className = "p-4" style = { { maxWidth : "400px" } } >
602+ { processedImages . length > 0 ? (
603+ < >
604+ < div className = "grid grid-cols-2 gap-2" >
605+ { processedImages
606+ . slice ( 0 , expandedImages ? undefined : maxInitialImages )
607+ . map ( ( imageUrl : string , index : number ) => (
567608 < div
568- className = "space-y-3"
569- style = { { maxWidth : "100%" , overflow : "hidden" } }
609+ key = { `img-${ index } ` }
610+ className = "relative border rounded-md overflow-hidden hover:border-blue-500 transition-colors cursor-pointer"
611+ onClick = { ( ) => handleImageClick ( imageUrl ) }
570612 >
571- { searchResults . map ( ( result , index ) => (
572- < SearchResultItem
573- key = { `result-${ index } ` }
574- result = { result }
575- />
576- ) ) }
613+ { renderImage ( imageUrl , index ) }
577614 </ div >
578- </ >
579- ) : (
580- < div className = "text-center text-gray-500 py-4 text-base" >
581- { t ( "chatRightPanel.noSearchResults" ) }
582- </ div >
583- ) }
615+ ) ) }
584616 </ div >
585- </ div >
586- </ StaticScrollArea >
587- ) ,
588- } ,
589- {
590- key : "images" ,
591- label : (
592- < span className = "flex items-center justify-center px-3 py-1.5 text-sm font-medium" >
593- { t ( "chatRightPanel.images" ) }
594- { processedImages . length > 0 && (
595- < span className = "ml-1 bg-gray-200 inline-flex items-center justify-center rounded px-1 text-xs font-medium min-w-[20px] h-[18px]" >
596- { processedImages . length }
597- </ span >
598- ) }
599- </ span >
600- ) ,
601- children : (
602- < StaticScrollArea
603- className = "flex-1"
604- style = { { maxWidth : "400px" , overflow : "hidden" } }
605- >
606- < div
607- className = "p-4"
608- style = { { maxWidth : "400px" , overflow : "hidden" } }
609- >
610- { processedImages . length > 0 ? (
611- < >
612- < div className = "grid grid-cols-2 gap-2" >
613- { processedImages
614- . slice ( 0 , expandedImages ? undefined : maxInitialImages )
615- . map ( ( imageUrl : string , index : number ) => (
616- < div
617- key = { `img-${ index } ` }
618- className = "relative border rounded-md overflow-hidden hover:border-blue-500 transition-colors cursor-pointer"
619- onClick = { ( ) => handleImageClick ( imageUrl ) }
620- >
621- { renderImage ( imageUrl , index ) }
622- </ div >
623- ) ) }
624- </ div >
625-
626- { processedImages . length > maxInitialImages && (
627- < div className = "mt-4 text-center" >
628- < Button
629- type = "default"
630- size = "small"
631- onClick = { ( ) => setExpandedImages ( ! expandedImages ) }
632- className = "w-full border border-slate-300 hover:border-slate-400 hover:bg-white transition-colors duration-200"
633- >
634- { expandedImages
635- ? t ( "chatRightPanel.collapseImages" )
636- : t ( "chatRightPanel.expandImages" , {
637- count : processedImages . length ,
638- } ) }
639- </ Button >
640- </ div >
641- ) }
642- </ >
643- ) : (
644- < div className = "flex flex-col items-center justify-center p-6 text-center min-h-[200px]" >
645- < Database className = "h-12 w-12 text-muted-foreground/40 mb-4" />
646- < p className = "text-lg font-medium mb-2" >
647- { t ( "chatRightPanel.noImages" ) }
648- </ p >
649- < p className = "text-sm text-muted-foreground" >
650- { t ( "chatRightPanel.noAssociatedImages" ) }
651- </ p >
617+
618+ { processedImages . length > maxInitialImages && (
619+ < div className = "mt-4 text-center" >
620+ < Button
621+ type = "default"
622+ size = "small"
623+ onClick = { ( ) => setExpandedImages ( ! expandedImages ) }
624+ className = "w-full"
625+ >
626+ { expandedImages
627+ ? t ( "chatRightPanel.collapseImages" )
628+ : t ( "chatRightPanel.expandImages" , {
629+ count : processedImages . length ,
630+ } ) }
631+ </ Button >
652632 </ div >
653633 ) }
634+ </ >
635+ ) : (
636+ < div className = "flex flex-col items-center justify-center p-6 text-center min-h-[200px]" >
637+ < Database className = "h-12 w-12 text-muted-foreground/40 mb-4" />
638+ < p className = "text-lg font-medium mb-2" >
639+ { t ( "chatRightPanel.noImages" ) }
640+ </ p >
641+ < p className = "text-sm text-muted-foreground" >
642+ { t ( "chatRightPanel.noAssociatedImages" ) }
643+ </ p >
654644 </ div >
655- </ StaticScrollArea >
656- ) ,
657- } ,
658- ] }
659- / >
645+ ) }
646+ </ div >
647+ ) }
648+ </ div >
649+ </ div >
660650 </ div >
661651 ) ;
662652}
0 commit comments