RowSelectionHeader.tsx114 lines · main
1import { AnimatePresence, motion } from 'framer-motion'
2import { Copy, X } from 'lucide-react'
3import { toast } from 'sonner'
4import {
5 copyToClipboard,
6 DropdownMenu,
7 DropdownMenuContent,
8 DropdownMenuItem,
9 DropdownMenuTrigger,
10} from 'ui'
11
12import { type LogData } from '../Settings/Logs/Logs.types'
13import {
14 buildLogsPrompt,
15 formatLogsAsJson,
16 formatLogsAsMarkdown,
17} from '../Settings/Logs/Logs.utils'
18import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
19import { AiAssistantDropdown } from '@/components/ui/AiAssistantDropdown'
20import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
21import { useDataTable } from '@/components/ui/DataTable/providers/DataTableProvider'
22import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state'
23import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state'
24
25// TODO - format Logs as JSON, as markdown, and as prompt
26
27export const RowSelectionHeader = () => {
28 const { openSidebar } = useSidebarManagerSnapshot()
29 const aiSnap = useAiAssistantStateSnapshot()
30
31 const { table } = useDataTable()
32 const selectedRows = table.getSelectedRowModel().rows.map((x) => x.original) as LogData[]
33
34 const handleOpenAiAssistant = () => {
35 const prompt = buildLogsPrompt(selectedRows)
36 openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
37 aiSnap.newChat({ initialMessage: prompt })
38 }
39
40 const onCopy = (format: 'json' | 'markdown') => {
41 const text =
42 format === 'json' ? formatLogsAsJson(selectedRows) : formatLogsAsMarkdown(selectedRows)
43 copyToClipboard(text, () => {
44 toast.success(
45 `Copied ${selectedRows.length} log${selectedRows.length !== 1 ? 's' : ''} as ${format.toUpperCase()}`
46 )
47 })
48 }
49
50 return (
51 <div className="relative">
52 <AnimatePresence>
53 {selectedRows.length > 0 && (
54 <motion.div
55 initial={{ height: 0, opacity: 0 }}
56 animate={{ height: '36px', opacity: 1 }}
57 exit={{ height: 0, opacity: 0 }}
58 transition={{
59 type: 'spring',
60 stiffness: 420,
61 damping: 30,
62 mass: 0.4,
63 }}
64 className="pl-4 pr-2 flex items-center justify-between border-t absolute top-0 z-99 bg-surface-75 w-full"
65 style={{ overflow: 'hidden' }}
66 >
67 <p className="text-xs">
68 {selectedRows.length} row{selectedRows.length > 1 ? 's' : ''} selected
69 </p>
70
71 <div className="flex items-center justify-center gap-x-1.5">
72 <DropdownMenu>
73 <DropdownMenuTrigger asChild>
74 <ButtonTooltip
75 type="default"
76 size="tiny"
77 icon={<Copy size={12} />}
78 className="w-7"
79 tooltip={{ content: { side: 'bottom', text: 'Copy selected logs' } }}
80 />
81 </DropdownMenuTrigger>
82 <DropdownMenuContent align="end" className="w-44">
83 <DropdownMenuItem onClick={() => onCopy('json')} className="gap-2 text-xs">
84 <Copy size={13} />
85 Copy as JSON
86 </DropdownMenuItem>
87 <DropdownMenuItem onClick={() => onCopy('markdown')} className="gap-2 text-xs">
88 <Copy size={13} />
89 Copy as Markdown
90 </DropdownMenuItem>
91 </DropdownMenuContent>
92 </DropdownMenu>
93
94 <AiAssistantDropdown
95 label="Explain with AI"
96 buildPrompt={() => buildLogsPrompt(selectedRows)}
97 onOpenAssistant={handleOpenAiAssistant}
98 telemetrySource="log_explorer"
99 />
100
101 <ButtonTooltip
102 type="text"
103 icon={<X />}
104 className="px-1"
105 onClick={() => table.resetRowSelection()}
106 tooltip={{ content: { side: 'bottom', text: 'Clear selection' } }}
107 />
108 </div>
109 </motion.div>
110 )}
111 </AnimatePresence>
112 </div>
113 )
114}