|
|
'use client' |
|
|
|
|
|
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react' |
|
|
import { useQueryState } from 'nuqs' |
|
|
|
|
|
import { useStore } from '@/store' |
|
|
import useSessionLoader from '@/hooks/useSessionLoader' |
|
|
|
|
|
import SessionItem from './SessionItem' |
|
|
import SessionBlankState from './SessionBlankState' |
|
|
import { Skeleton } from '@/components/ui/skeleton' |
|
|
import { cn } from '@/lib/utils' |
|
|
|
|
|
interface SkeletonListProps { |
|
|
skeletonCount: number |
|
|
} |
|
|
const SkeletonList: FC<SkeletonListProps> = ({ skeletonCount }) => { |
|
|
const list = useMemo( |
|
|
() => Array.from({ length: skeletonCount }, (_, i) => i), |
|
|
[skeletonCount] |
|
|
) |
|
|
|
|
|
return list.map((k, idx) => ( |
|
|
<Skeleton |
|
|
key={k} |
|
|
className={cn( |
|
|
'mb-1 h-11 rounded-lg px-3 py-2', |
|
|
idx > 0 && 'bg-background-secondary' |
|
|
)} |
|
|
/> |
|
|
)) |
|
|
} |
|
|
|
|
|
const Sessions = () => { |
|
|
const [agentId] = useQueryState('agent', { |
|
|
parse: (v) => v || undefined, |
|
|
history: 'push' |
|
|
}) |
|
|
const [teamId] = useQueryState('team') |
|
|
const [sessionId] = useQueryState('session') |
|
|
const [dbId] = useQueryState('db_id') |
|
|
|
|
|
const { |
|
|
selectedEndpoint, |
|
|
mode, |
|
|
isEndpointActive, |
|
|
isEndpointLoading, |
|
|
hydrated, |
|
|
sessionsData, |
|
|
setSessionsData, |
|
|
isSessionsLoading |
|
|
} = useStore() |
|
|
|
|
|
console.log({ sessionsData }) |
|
|
|
|
|
const [isScrolling, setIsScrolling] = useState(false) |
|
|
const [selectedSessionId, setSelectedSessionId] = useState<string | null>( |
|
|
null |
|
|
) |
|
|
|
|
|
const { getSessions, getSession } = useSessionLoader() |
|
|
const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout>>(null) |
|
|
|
|
|
const handleScroll = () => { |
|
|
setIsScrolling(true) |
|
|
|
|
|
if (scrollTimeoutRef.current) { |
|
|
clearTimeout(scrollTimeoutRef.current) |
|
|
} |
|
|
|
|
|
scrollTimeoutRef.current = setTimeout(() => { |
|
|
setIsScrolling(false) |
|
|
}, 1500) |
|
|
} |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
return () => { |
|
|
if (scrollTimeoutRef.current) { |
|
|
clearTimeout(scrollTimeoutRef.current) |
|
|
} |
|
|
} |
|
|
}, []) |
|
|
|
|
|
useEffect(() => { |
|
|
if (hydrated && sessionId && selectedEndpoint && (agentId || teamId)) { |
|
|
const entityType = agentId ? 'agent' : 'team' |
|
|
getSession({ entityType, agentId, teamId, dbId }, sessionId) |
|
|
} |
|
|
|
|
|
}, [hydrated, sessionId, selectedEndpoint, agentId, teamId, dbId]) |
|
|
|
|
|
useEffect(() => { |
|
|
if (!selectedEndpoint || isEndpointLoading) return |
|
|
if (!(agentId || teamId || dbId)) { |
|
|
setSessionsData([]) |
|
|
return |
|
|
} |
|
|
setSessionsData([]) |
|
|
getSessions({ |
|
|
entityType: mode, |
|
|
agentId, |
|
|
teamId, |
|
|
dbId |
|
|
}) |
|
|
|
|
|
}, [ |
|
|
selectedEndpoint, |
|
|
agentId, |
|
|
teamId, |
|
|
mode, |
|
|
isEndpointLoading, |
|
|
getSessions, |
|
|
dbId |
|
|
]) |
|
|
|
|
|
useEffect(() => { |
|
|
if (sessionId) setSelectedSessionId(sessionId) |
|
|
}, [sessionId]) |
|
|
|
|
|
const handleSessionClick = useCallback( |
|
|
(id: string) => () => setSelectedSessionId(id), |
|
|
[] |
|
|
) |
|
|
|
|
|
if (isSessionsLoading || isEndpointLoading) { |
|
|
return ( |
|
|
<div className="w-full"> |
|
|
<div className="mb-2 text-xs font-medium uppercase">Sessions</div> |
|
|
<div className="mt-4 h-[calc(100vh-325px)] w-full overflow-y-auto"> |
|
|
<SkeletonList skeletonCount={5} /> |
|
|
</div> |
|
|
</div> |
|
|
) |
|
|
} |
|
|
|
|
|
return ( |
|
|
<div className="w-full"> |
|
|
<div className="mb-2 w-full text-xs font-medium uppercase">Sessions</div> |
|
|
<div |
|
|
className={`h-[calc(100vh-345px)] overflow-y-auto font-geist transition-all duration-300 [&::-webkit-scrollbar]:w-1 [&::-webkit-scrollbar]:transition-opacity [&::-webkit-scrollbar]:duration-300 ${ |
|
|
isScrolling |
|
|
? '[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-background [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar]:opacity-0' |
|
|
: '[&::-webkit-scrollbar]:opacity-100' |
|
|
}`} |
|
|
onScroll={handleScroll} |
|
|
onMouseOver={() => setIsScrolling(true)} |
|
|
onMouseLeave={handleScroll} |
|
|
> |
|
|
{!isEndpointActive || |
|
|
(!isSessionsLoading && |
|
|
(!sessionsData || sessionsData?.length === 0)) ? ( |
|
|
<SessionBlankState /> |
|
|
) : ( |
|
|
<div className="flex flex-col gap-y-1 pr-1"> |
|
|
{sessionsData?.map((entry, idx) => ( |
|
|
<SessionItem |
|
|
key={`${entry?.session_id}-${idx}`} |
|
|
currentSessionId={selectedSessionId} |
|
|
isSelected={selectedSessionId === entry?.session_id} |
|
|
onSessionClick={handleSessionClick(entry?.session_id)} |
|
|
session_name={entry?.session_name ?? '-'} |
|
|
session_id={entry?.session_id} |
|
|
created_at={entry?.created_at} |
|
|
/> |
|
|
))} |
|
|
</div> |
|
|
)} |
|
|
</div> |
|
|
</div> |
|
|
) |
|
|
} |
|
|
|
|
|
export default Sessions |
|
|
|