LogsExplorerHeader.tsx213 lines · main
1import { BookOpen, Check, ChevronsUpDown, Copy, ExternalLink, List, X } from 'lucide-react'
2import Link from 'next/link'
3import { useState } from 'react'
4import { logConstants } from 'shared-data'
5import {
6 Button,
7 cn,
8 Command,
9 CommandEmpty,
10 CommandGroup,
11 CommandInput,
12 CommandItem,
13 CommandList,
14 copyToClipboard,
15 Popover,
16 PopoverContent,
17 PopoverTrigger,
18 SidePanel,
19 Tooltip,
20 TooltipContent,
21 TooltipTrigger,
22} from 'ui'
23
24import { DocsButton } from '../DocsButton'
25import { LOGS_EXPLORER_DOCS_URL } from '@/components/interfaces/Settings/Logs/Logs.constants'
26import Table from '@/components/to-be-cleaned/Table'
27import { DOCS_URL } from '@/lib/constants'
28
29export interface LogsExplorerHeaderProps {
30 subtitle?: string
31}
32
33const LogsExplorerHeader = ({ subtitle }: LogsExplorerHeaderProps) => {
34 const [showReference, setShowReference] = useState(false)
35 const [open, setOpen] = useState(false)
36 const [selectedSchema, setSelectedSchema] = useState(logConstants.schemas[0])
37
38 return (
39 <div className="flex flex-col md:flex-row md:items-center gap-4 md:gap-8 transition-all pb-6 justify-between">
40 <div className="flex flex-col md:flex-row md:items-center gap-3">
41 <div className="flex flex-row items-center gap-3">
42 <div className="flex h-6 w-6 items-center justify-center rounded-sm border border-brand-600 bg-brand-300 text-brand">
43 <List size={14} strokeWidth={3} />
44 </div>
45
46 <h1>Logs Explorer</h1>
47 </div>
48 {subtitle && <span className="text-2xl text-foreground-light">{subtitle}</span>}
49 </div>
50 <div className="flex flex-row gap-2">
51 <DocsButton href={LOGS_EXPLORER_DOCS_URL} />
52
53 <SidePanel
54 size="large"
55 header={
56 <div className="flex flex-row justify-between items-center">
57 <h3>Field Reference</h3>
58 <Button
59 type="text"
60 className="px-1"
61 onClick={() => setShowReference(false)}
62 icon={<X size={18} strokeWidth={1.5} />}
63 />
64 </div>
65 }
66 visible={showReference}
67 cancelText="Close"
68 onCancel={() => setShowReference(false)}
69 hideFooter
70 triggerElement={
71 <Button
72 type="default"
73 onClick={() => setShowReference(true)}
74 icon={<BookOpen strokeWidth={1.5} />}
75 >
76 <span>Field Reference</span>
77 </Button>
78 }
79 >
80 <SidePanel.Content>
81 <div className="pt-4 pb-2 space-y-1">
82 <p className="text-sm">
83 The following table shows all the available paths that can be queried from each
84 respective source. Do note that to access nested keys, you would need to perform the
85 necessary{' '}
86 <Link
87 href={`${DOCS_URL}/guides/platform/logs#unnesting-arrays`}
88 target="_blank"
89 rel="noreferrer"
90 className="text-brand"
91 >
92 unnesting joins
93 <ExternalLink
94 size={14}
95 className="ml-1 inline translate-y-[-2px]"
96 strokeWidth={1.5}
97 />
98 </Link>
99 </p>
100 </div>
101 </SidePanel.Content>
102 <SidePanel.Separator />
103 <div className="px-4 pb-4 flex flex-col gap-4">
104 <Popover open={open} onOpenChange={setOpen}>
105 <PopoverTrigger asChild>
106 <Button
107 type="default"
108 role="combobox"
109 size={'small'}
110 aria-expanded={open}
111 className="w-full justify-between"
112 iconRight={<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />}
113 >
114 {selectedSchema?.name ?? 'Select source...'}
115 </Button>
116 </PopoverTrigger>
117 <PopoverContent className="p-0" sameWidthAsTrigger>
118 <Command>
119 <CommandInput placeholder="Search source..." />
120 <CommandList>
121 <CommandEmpty>No source found.</CommandEmpty>
122 <CommandGroup>
123 {logConstants.schemas.map((schema) => (
124 <CommandItem
125 key={schema.reference}
126 value={schema.reference}
127 onSelect={() => {
128 setSelectedSchema(schema)
129 setOpen(false)
130 }}
131 >
132 <Check
133 className={cn(
134 'mr-2 h-4 w-4',
135 selectedSchema === schema ? 'opacity-100' : 'opacity-0'
136 )}
137 />
138 {schema.name}
139 </CommandItem>
140 ))}
141 </CommandGroup>
142 </CommandList>
143 </Command>
144 </PopoverContent>
145 </Popover>
146 <Table
147 head={[
148 <Table.th className="text-xs p-2!" key="path">
149 Path
150 </Table.th>,
151 <Table.th key="type" className="text-xs p-2!">
152 Type
153 </Table.th>,
154 ]}
155 body={selectedSchema.fields.map((field) => (
156 <Field key={field.path} field={field} />
157 ))}
158 />
159 </div>
160 </SidePanel>
161 </div>
162 </div>
163 )
164}
165
166export default LogsExplorerHeader
167
168const Field = ({
169 field,
170}: {
171 field: {
172 path: string
173 type: string
174 }
175}) => {
176 const [isCopied, setIsCopied] = useState(false)
177
178 return (
179 <Table.tr>
180 <Table.td
181 className="font-mono text-xs p-2! cursor-pointer hover:text-foreground transition flex items-center space-x-2"
182 onClick={() =>
183 copyToClipboard(field.path, () => {
184 setIsCopied(true)
185 setTimeout(() => setIsCopied(false), 3000)
186 })
187 }
188 >
189 <span>{field.path}</span>
190 {isCopied ? (
191 <Tooltip>
192 <TooltipTrigger>
193 <Check size={14} strokeWidth={3} className="text-brand" />
194 </TooltipTrigger>
195 <TooltipContent side="bottom" className="font-sans">
196 Copied
197 </TooltipContent>
198 </Tooltip>
199 ) : (
200 <Tooltip>
201 <TooltipTrigger>
202 <Copy size={14} />
203 </TooltipTrigger>
204 <TooltipContent side="bottom" className="font-sans">
205 Copy value
206 </TooltipContent>
207 </Tooltip>
208 )}
209 </Table.td>
210 <Table.td className="font-mono text-xs p-2!">{field.type}</Table.td>
211 </Table.tr>
212 )
213}