diff --git a/ui/src/components/session/sidebar/SessionNodeItem.tsx b/ui/src/components/session/sidebar/SessionNodeItem.tsx index 83f5f95..2cd9067 100644 --- a/ui/src/components/session/sidebar/SessionNodeItem.tsx +++ b/ui/src/components/session/sidebar/SessionNodeItem.tsx @@ -14,6 +14,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip import { GridLoader } from '@/components/ui/grid-loader'; import { RiAddLine, + RiArchiveLine, RiArrowDownSLine, RiArrowRightSLine, RiChat4Line, @@ -34,6 +35,8 @@ import { RiUnpinLine, } from '@remixicon/react'; import { cn } from '@/lib/utils'; +import { useSessionStore } from '@/stores/useSessionStore'; +import { toast } from '@/components/ui'; import { DraggableSessionRow } from './sessionFolderDnd'; import type { SessionNode, SessionSummaryMeta } from './types'; import { formatSessionDateLabel, normalizePath, renderHighlightedText, resolveSessionDiffStats } from './utils'; @@ -159,6 +162,99 @@ export function SessionNodeItem(props: Props): React.ReactNode { const sessionSummary = session.summary as SessionSummaryMeta | undefined; const sessionDiffStats = resolveSessionDiffStats(sessionSummary); + const archiveSessions = useSessionStore((state) => state.archiveSessions); + const sessionsByDirectory = useSessionStore((state) => state.sessionsByDirectory); + const deleteSessions = useSessionStore((state) => state.deleteSessions); + const archivedSessions = useSessionStore((state) => state.archivedSessions); + + const handleArchiveOthers = React.useCallback(async () => { + if (!sessionDirectory) return; + + const sessionsInDir = sessionsByDirectory.get(sessionDirectory) ?? []; + const otherSessions = sessionsInDir.filter(s => s.id !== session.id && !s.time?.archived); + const ids = otherSessions.map(s => s.id); + + if (ids.length === 0) { + toast.message('No other sessions to archive in this directory'); + return; + } + + const result = await archiveSessions(ids, { silent: true }); + if (result.archivedIds.length > 0) { + toast.success(`Archived ${result.archivedIds.length} session${result.archivedIds.length === 1 ? '' : 's'}`); + } + if (result.failedIds.length > 0) { + toast.error(`Failed to archive ${result.failedIds.length} session${result.failedIds.length === 1 ? '' : 's'}`); + } + }, [sessionDirectory, session.id, sessionsByDirectory, archiveSessions]); + + const handleArchiveAll = React.useCallback(async () => { + if (!sessionDirectory) return; + + const sessionsInDir = sessionsByDirectory.get(sessionDirectory) ?? []; + const allSessions = sessionsInDir.filter(s => !s.time?.archived); + const ids = allSessions.map(s => s.id); + + if (ids.length === 0) { + toast.message('No sessions to archive in this directory'); + return; + } + + const result = await archiveSessions(ids, { silent: true }); + if (result.archivedIds.length > 0) { + toast.success(`Archived ${result.archivedIds.length} session${result.archivedIds.length === 1 ? '' : 's'}`); + } + if (result.failedIds.length > 0) { + toast.error(`Failed to archive ${result.failedIds.length} session${result.failedIds.length === 1 ? '' : 's'}`); + } + }, [sessionDirectory, sessionsByDirectory, archiveSessions]); + + const handleDeleteOthers = React.useCallback(async () => { + if (!sessionDirectory) return; + + const otherSessions = archivedSessions.filter(s => { + const sessionDir = normalizePath((s as Session & { directory?: string | null }).directory ?? null); + return sessionDir === sessionDirectory && s.id !== session.id; + }); + const ids = otherSessions.map(s => s.id); + + if (ids.length === 0) { + toast.message('No other sessions to delete in this directory'); + return; + } + + const result = await deleteSessions(ids, { silent: true }); + if (result.deletedIds.length > 0) { + toast.success(`Deleted ${result.deletedIds.length} session${result.deletedIds.length === 1 ? '' : 's'}`); + } + if (result.failedIds.length > 0) { + toast.error(`Failed to delete ${result.failedIds.length} session${result.failedIds.length === 1 ? '' : 's'}`); + } + }, [sessionDirectory, session.id, archivedSessions, deleteSessions]); + + const handleDeleteAll = React.useCallback(async () => { + if (!sessionDirectory) return; + + const allSessions = archivedSessions.filter(s => { + const sessionDir = normalizePath((s as Session & { directory?: string | null }).directory ?? null); + return sessionDir === sessionDirectory; + }); + const ids = allSessions.map(s => s.id); + + if (ids.length === 0) { + toast.message('No sessions to delete in this directory'); + return; + } + + const result = await deleteSessions(ids, { silent: true }); + if (result.deletedIds.length > 0) { + toast.success(`Deleted ${result.deletedIds.length} session${result.deletedIds.length === 1 ? '' : 's'}`); + } + if (result.failedIds.length > 0) { + toast.error(`Failed to delete ${result.failedIds.length} session${result.failedIds.length === 1 ? '' : 's'}`); + } + }, [sessionDirectory, archivedSessions, deleteSessions]); + if (editingId === session.id) { return (
+ {!archivedBucket && ( + <> + + + Archive Others + + + + Archive All + + + + )} + {archivedBucket && ( + <> + + + Delete Others + + + + Delete All + + + + )} handleDeleteSession(session, { archivedBucket })}> {archivedBucket ? 'Delete' : 'Archive'}