mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
cleaned up code
This commit is contained in:
1 parent
5db0514624
commit
3ca2322e35
12 files changed
+175
-346
No files matched your search
@@ -11,7 +11,6 @@ import {
|
||||
IconSearch,
|
||||
IconClock,
|
||||
Popover,
|
||||
IconPlay,
|
||||
} from '@supabase/ui'
|
||||
import { LogSearchCallback, LogTemplate } from '.'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -30,8 +29,6 @@ interface Props {
|
||||
onSelectTemplate: (template: LogTemplate) => void
|
||||
isShowingEventChart: boolean
|
||||
onToggleEventChart: () => void
|
||||
|
||||
editorControls: React.ReactNode
|
||||
}
|
||||
|
||||
dayjs.extend(utc)
|
||||
@@ -53,8 +50,6 @@ const LogPanel: FC<Props> = ({
|
||||
onSelectTemplate,
|
||||
isShowingEventChart,
|
||||
onToggleEventChart,
|
||||
|
||||
editorControls,
|
||||
}) => {
|
||||
const [search, setSearch] = useState('')
|
||||
const [to, setTo] = useState({ value: '', error: '' })
|
||||
@@ -266,7 +261,6 @@ const LogPanel: FC<Props> = ({
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{editorControls}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ import DatabasePostgresSelectionRender from './LogSelectionRenderers/DatabasePos
|
||||
interface Props {
|
||||
log: LogData
|
||||
onClose: () => void
|
||||
queryType: QueryType
|
||||
queryType?: QueryType
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,11 +38,9 @@ const LogSelection: FC<Props> = ({ log, onClose, queryType }) => {
|
||||
switch (queryType) {
|
||||
case 'api':
|
||||
return DatabaseApiSelectionHeaderRender(log)
|
||||
break
|
||||
|
||||
default:
|
||||
return null
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import dayjs from 'dayjs'
|
||||
import { useEffect, useState, useMemo } from 'react'
|
||||
import { Badge, Button, IconDownload, IconDownloadCloud, IconEye, Typography } from '@supabase/ui'
|
||||
import DataGrid, { Row } from '@supabase/react-data-grid'
|
||||
import { Button, IconDownloadCloud, IconEye, Typography } from '@supabase/ui'
|
||||
import DataGrid from '@supabase/react-data-grid'
|
||||
|
||||
import LogSelection from './LogSelection'
|
||||
import { LogData, Mode, QueryType } from './Logs.types'
|
||||
import { isNil } from 'lodash'
|
||||
import { LogData, QueryType } from './Logs.types'
|
||||
import { SeverityFormatter, ResponseCodeFormatter, HeaderFormmater } from './LogsFormatters'
|
||||
|
||||
// column renders
|
||||
@@ -15,7 +14,7 @@ import DatabasePostgresColumnRender from './LogColumnRenderers/DatabasePostgresC
|
||||
interface Props {
|
||||
isCustomQuery: boolean
|
||||
data?: Array<LogData | Object>
|
||||
queryType: QueryType
|
||||
queryType?: QueryType
|
||||
}
|
||||
type LogMap = { [id: string]: LogData }
|
||||
|
||||
@@ -24,193 +23,130 @@ type LogMap = { [id: string]: LogData }
|
||||
*
|
||||
* When in custom data display mode, the side panel will not open when focusing on logs.
|
||||
*/
|
||||
const LogTable = ({ isCustomQuery, data = [], queryType }: Props) => {
|
||||
const LogTable = ({ data = [], queryType }: Props) => {
|
||||
const [focusedLog, setFocusedLog] = useState<LogData | null>(null)
|
||||
const columnNames = Object.keys(data[0] || {})
|
||||
const hasId = columnNames.includes('id')
|
||||
const hasTimestamp = columnNames.includes('timestamp')
|
||||
|
||||
// console.log('queryType', queryType)
|
||||
|
||||
// console.log('focusedLog', focusedLog)
|
||||
|
||||
isCustomQuery = false
|
||||
|
||||
// whether the data structure is LogData format.
|
||||
// const hasLogDataFormat =
|
||||
// columnNames.includes('timestamp') &&
|
||||
// columnNames.includes('event_message') &&
|
||||
// columnNames.length === 4
|
||||
|
||||
const hasLogDataFormat = !isCustomQuery
|
||||
|
||||
const DEFAULT_COLUMNS = (hasLogDataFormat ? ['timestamp', 'event_message'] : columnNames).map(
|
||||
(v) => ({
|
||||
key: v,
|
||||
name: v,
|
||||
// width: hasLogDataFormat && v === 'timestamp' ? 210 : undefined,
|
||||
resizable: true,
|
||||
// headerRenderer: () => {
|
||||
// return <div className="flex items-center text-xs font-mono h-full">{v}</div>
|
||||
// },
|
||||
// formatter: ({ row }: any) => {
|
||||
// let value = row[v]
|
||||
// if (hasLogDataFormat && v === 'timestamp') {
|
||||
// value = dayjs(Number(row['timestamp']) / 1000).toISOString()
|
||||
// }
|
||||
// return (
|
||||
// <p
|
||||
// className={[
|
||||
// 'block whitespace-wrap font-mono',
|
||||
// `${hasLogDataFormat && row.id === focusedLog?.id ? 'font-bold' : ''}`,
|
||||
// `${hasLogDataFormat && v === 'timestamp' ? 'text-green-900' : ''}`,
|
||||
// ].join(' ')}
|
||||
// >
|
||||
// {value}
|
||||
// </p>
|
||||
// )
|
||||
// },
|
||||
})
|
||||
)
|
||||
|
||||
const DEFAULT_COLUMNS = columnNames.map((v) => ({ key: v, name: v, resizable: true }))
|
||||
let columns
|
||||
|
||||
// console.log('QUERY TYPE', queryType)
|
||||
|
||||
switch (queryType) {
|
||||
case 'api':
|
||||
if (isCustomQuery) {
|
||||
columns = DEFAULT_COLUMNS
|
||||
if (!queryType) {
|
||||
columns = DEFAULT_COLUMNS
|
||||
} else {
|
||||
switch (queryType) {
|
||||
case 'api':
|
||||
columns = DatabaseApiColumnRender
|
||||
break
|
||||
}
|
||||
|
||||
// console.log('is api, running column')
|
||||
columns = DatabaseApiColumnRender
|
||||
|
||||
break
|
||||
|
||||
case 'database':
|
||||
if (isCustomQuery) {
|
||||
columns = DEFAULT_COLUMNS
|
||||
case 'database':
|
||||
columns = DatabasePostgresColumnRender
|
||||
break
|
||||
}
|
||||
|
||||
// console.log('is api, running column')
|
||||
columns = DatabasePostgresColumnRender
|
||||
|
||||
break
|
||||
|
||||
case 'fn_edge':
|
||||
if (isCustomQuery) {
|
||||
columns = DEFAULT_COLUMNS
|
||||
break
|
||||
}
|
||||
|
||||
columns = [
|
||||
{
|
||||
key: 'timestamp',
|
||||
headerRenderer: () => (
|
||||
<div className="flex w-full justify-end h-full">
|
||||
<HeaderFormmater value={'timestamp'} />
|
||||
</div>
|
||||
),
|
||||
name: 'timestamp',
|
||||
formatter: (data: any) => (
|
||||
<span className="flex w-full h-full items-center gap-1">
|
||||
<span className="text-xs">{dayjs(data?.row?.timestamp / 1000).format('DD MMM')}</span>
|
||||
<span className="text-xs">
|
||||
{dayjs(data?.row?.timestamp / 1000).format('HH:mm:ss')}
|
||||
case 'fn_edge':
|
||||
columns = [
|
||||
{
|
||||
key: 'timestamp',
|
||||
headerRenderer: () => (
|
||||
<div className="flex w-full justify-end h-full">
|
||||
<HeaderFormmater value={'timestamp'} />
|
||||
</div>
|
||||
),
|
||||
name: 'timestamp',
|
||||
formatter: (data: any) => (
|
||||
<span className="flex w-full h-full items-center gap-1">
|
||||
<span className="text-xs">
|
||||
{dayjs(data?.row?.timestamp / 1000).format('DD MMM')}
|
||||
</span>
|
||||
<span className="text-xs">
|
||||
{dayjs(data?.row?.timestamp / 1000).format('HH:mm:ss')}
|
||||
</span>
|
||||
</span>
|
||||
{/* {data?.row?.timestamp} */}
|
||||
</span>
|
||||
),
|
||||
width: 128,
|
||||
},
|
||||
{
|
||||
key: 'status_code',
|
||||
headerRenderer: () => <HeaderFormmater value={'Status'} />,
|
||||
name: 'status_code',
|
||||
formatter: (data: any) => (
|
||||
<ResponseCodeFormatter row={data} value={data.row.status_code} />
|
||||
),
|
||||
),
|
||||
width: 128,
|
||||
},
|
||||
{
|
||||
key: 'status_code',
|
||||
headerRenderer: () => <HeaderFormmater value={'Status'} />,
|
||||
name: 'status_code',
|
||||
formatter: (data: any) => (
|
||||
<ResponseCodeFormatter row={data} value={data.row.status_code} />
|
||||
),
|
||||
width: 0,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
key: 'method',
|
||||
headerRenderer: () => <HeaderFormmater value={'method'} />,
|
||||
width: 0,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
key: 'id',
|
||||
headerRenderer: () => <HeaderFormmater value={'id'} />,
|
||||
name: 'id',
|
||||
resizable: true,
|
||||
},
|
||||
]
|
||||
break
|
||||
case 'functions':
|
||||
columns = [
|
||||
{
|
||||
key: 'timestamp',
|
||||
headerRenderer: () => (
|
||||
<div className="flex w-full justify-end h-full">
|
||||
<HeaderFormmater value={'timestamp'} />
|
||||
</div>
|
||||
),
|
||||
name: 'timestamp',
|
||||
formatter: (data: any) => (
|
||||
<span className="flex w-full h-full items-center gap-1">
|
||||
<span className="text-xs !text-scale-1100">
|
||||
{dayjs(data?.row?.timestamp / 1000).format('DD MMM')}
|
||||
</span>
|
||||
<span className="text-xs !text-scale-1100">
|
||||
{dayjs(data?.row?.timestamp / 1000).format('HH:mm:ss')}
|
||||
</span>
|
||||
</span>
|
||||
),
|
||||
width: 128,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
key: 'level',
|
||||
headerRenderer: () => <HeaderFormmater value={'Level'} />,
|
||||
name: 'level',
|
||||
formatter: (data: any) => <SeverityFormatter value={data.row.level} />,
|
||||
width: 24,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
key: 'event_message',
|
||||
headerRenderer: () => <HeaderFormmater value={'Event message'} />,
|
||||
resizable: true,
|
||||
},
|
||||
]
|
||||
break
|
||||
|
||||
width: 0,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
key: 'method',
|
||||
headerRenderer: () => <HeaderFormmater value={'method'} />,
|
||||
width: 0,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
key: 'id',
|
||||
headerRenderer: () => <HeaderFormmater value={'id'} />,
|
||||
name: 'id',
|
||||
resizable: true,
|
||||
},
|
||||
]
|
||||
break
|
||||
case 'functions':
|
||||
if (isCustomQuery) {
|
||||
default:
|
||||
columns = DEFAULT_COLUMNS
|
||||
break
|
||||
}
|
||||
columns = [
|
||||
{
|
||||
key: 'timestamp',
|
||||
headerRenderer: () => (
|
||||
<div className="flex w-full justify-end h-full">
|
||||
<HeaderFormmater value={'timestamp'} />
|
||||
</div>
|
||||
),
|
||||
name: 'timestamp',
|
||||
formatter: (data: any) => (
|
||||
<span className="flex w-full h-full items-center gap-1">
|
||||
<span className="text-xs !text-scale-1100">
|
||||
{dayjs(data?.row?.timestamp / 1000).format('DD MMM')}
|
||||
</span>
|
||||
<span className="text-xs !text-scale-1100">
|
||||
{dayjs(data?.row?.timestamp / 1000).format('HH:mm:ss')}
|
||||
</span>
|
||||
{/* {data?.row?.timestamp} */}
|
||||
</span>
|
||||
),
|
||||
width: 128,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
key: 'level',
|
||||
headerRenderer: () => <HeaderFormmater value={'Level'} />,
|
||||
name: 'level',
|
||||
formatter: (data: any) => <SeverityFormatter value={data.row.level} />,
|
||||
width: 24,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
key: 'event_message',
|
||||
headerRenderer: () => <HeaderFormmater value={'Event message'} />,
|
||||
resizable: true,
|
||||
},
|
||||
]
|
||||
break
|
||||
|
||||
default:
|
||||
columns = DEFAULT_COLUMNS
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const stringData = JSON.stringify(data)
|
||||
const logMap = useMemo(() => {
|
||||
if (!hasLogDataFormat) return {} as LogMap
|
||||
if (!hasId) return {} as LogMap
|
||||
const logData = data as LogData[]
|
||||
return logData.reduce((acc: LogMap, d: LogData) => {
|
||||
acc[d.id] = d
|
||||
return acc
|
||||
}, {}) as LogMap
|
||||
}, [JSON.stringify(data)])
|
||||
}, [stringData])
|
||||
|
||||
const stringData = JSON.stringify(data)
|
||||
useEffect(() => {
|
||||
if (!hasLogDataFormat) return
|
||||
if (isNil(data)) return
|
||||
if (!hasId || data === null) return
|
||||
if (focusedLog && !(focusedLog.id in logMap)) {
|
||||
setFocusedLog(null)
|
||||
}
|
||||
@@ -218,20 +154,21 @@ const LogTable = ({ isCustomQuery, data = [], queryType }: Props) => {
|
||||
|
||||
if (!data) return null
|
||||
|
||||
// console.log(data)
|
||||
// console.log('logMap', logMap)
|
||||
// console.log(columns)
|
||||
|
||||
// [Joshen] Hmm quite hacky now, but will do
|
||||
const maxHeight = isCustomQuery ? 'calc(100vh - 42px - 10rem)' : 'calc(100vh - 42px - 3rem)'
|
||||
const maxHeight = !queryType ? 'calc(100vh - 42px - 10rem)' : 'calc(100vh - 42px - 3rem)'
|
||||
|
||||
const logDataRows = useMemo(() => {
|
||||
if (!hasLogDataFormat) return data
|
||||
return Object.values(logMap).sort((a, b) => b.timestamp - a.timestamp)
|
||||
if (hasId && hasTimestamp) {
|
||||
return Object.values(logMap).sort((a, b) => b.timestamp - a.timestamp)
|
||||
} else {
|
||||
const deduped = [...new Set(data)]
|
||||
return deduped
|
||||
}
|
||||
}, [stringData])
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCustomQuery && (
|
||||
{!queryType && (
|
||||
<div
|
||||
className="
|
||||
w-full bg-scale-300 rounded
|
||||
@@ -264,12 +201,12 @@ const LogTable = ({ isCustomQuery, data = [], queryType }: Props) => {
|
||||
style={{ height: '100%' }}
|
||||
className={`
|
||||
flex-grow flex-1
|
||||
${isCustomQuery ? '' : ' data-grid--simple-logs'}
|
||||
${false ? '' : ' data-grid--simple-logs'}
|
||||
`}
|
||||
rowHeight={40}
|
||||
headerRowHeight={0}
|
||||
onSelectedCellChange={({ idx, rowIdx }) => {
|
||||
if (!hasLogDataFormat) return
|
||||
if (!hasId) return
|
||||
setFocusedLog(data[rowIdx] as LogData)
|
||||
}}
|
||||
noRowsFallback={
|
||||
@@ -281,22 +218,9 @@ const LogTable = ({ isCustomQuery, data = [], queryType }: Props) => {
|
||||
}
|
||||
columns={columns as any}
|
||||
rowClass={(r) => {
|
||||
// if (!hasLogDataFormat) return 'cursor-pointer'
|
||||
const row = r as LogData
|
||||
|
||||
let classes = []
|
||||
|
||||
//@ts-ignore
|
||||
// if (row.status_code) {
|
||||
// const response = parseResponseCode(row.status_code)
|
||||
// if (response == 'warning') {
|
||||
// classes.push('!bg-yellow-200 !dark:bg-yellow-100')
|
||||
// }
|
||||
// if (response == 'error') {
|
||||
// classes.push('!bg-red-200')
|
||||
// }
|
||||
// }
|
||||
|
||||
classes.push(
|
||||
`${row.id === focusedLog?.id ? '!bg-scale-400 rdg-row--focussed' : 'cursor-pointer'}`
|
||||
)
|
||||
@@ -305,7 +229,7 @@ const LogTable = ({ isCustomQuery, data = [], queryType }: Props) => {
|
||||
}}
|
||||
rows={logDataRows}
|
||||
rowKeyGetter={(r) => {
|
||||
if (!hasLogDataFormat) return Object.keys(r)[0]
|
||||
if (!hasId) return Object.keys(r)[0]
|
||||
const row = r as LogData
|
||||
return row.id
|
||||
}}
|
||||
@@ -315,16 +239,15 @@ const LogTable = ({ isCustomQuery, data = [], queryType }: Props) => {
|
||||
setFocusedLog(logMap[row.id])
|
||||
}}
|
||||
/>
|
||||
{/* {hasLogDataFormat ||
|
||||
(focusedLog && ( */}
|
||||
<div className="w-1/2 flex flex-col">
|
||||
<LogSelection
|
||||
onClose={() => setFocusedLog(null)}
|
||||
log={focusedLog}
|
||||
queryType={queryType}
|
||||
/>
|
||||
</div>
|
||||
{/* ))} */}
|
||||
{hasId && focusedLog && (
|
||||
<div className="w-1/2 flex flex-col">
|
||||
<LogSelection
|
||||
onClose={() => setFocusedLog(null)}
|
||||
log={focusedLog}
|
||||
queryType={queryType}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -209,7 +209,23 @@ export const genQueryParams = (params: { [k: string]: string }) => {
|
||||
return qs
|
||||
}
|
||||
|
||||
export const FILTER_OPTIONS: any = {
|
||||
|
||||
interface Filter {
|
||||
label: string
|
||||
key: string
|
||||
options: {
|
||||
key: string
|
||||
label: string
|
||||
description?: string
|
||||
}[]
|
||||
}
|
||||
type FilterOptions = {
|
||||
[table: string] : {
|
||||
[filterName: string]: Filter
|
||||
}
|
||||
}
|
||||
|
||||
export const FILTER_OPTIONS: FilterOptions = {
|
||||
// Postgres logs
|
||||
postgres_logs: {
|
||||
severity: {
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
import { Severity } from '@sentry/browser'
|
||||
import { filter } from 'lodash'
|
||||
import { LogsTableName, SQL_FILTER_TEMPLATES } from './Logs.constants'
|
||||
import { FilterObject } from './Logs.types'
|
||||
|
||||
export function filterSqlWhereBuilder(
|
||||
filters: FilterObject,
|
||||
filters: FilterObject | any,
|
||||
table: LogsTableName,
|
||||
searchQuery: string
|
||||
) {
|
||||
// console.log('raw filters', filters)
|
||||
|
||||
// remove any filter arrays that are empty
|
||||
const filtersSanitized: any = Object.values(filters).filter((x) => x && x.length > 0)
|
||||
|
||||
// console.log('filtersSanitized', filtersSanitized)
|
||||
const filtersSanitized: any = Object.values(filters).filter((x: any) => x && x.length > 0)
|
||||
|
||||
const keys = Object.keys(filters)
|
||||
|
||||
@@ -25,17 +18,13 @@ export function filterSqlWhereBuilder(
|
||||
|
||||
keys.map((x: string, i) => {
|
||||
// do not parse empty key
|
||||
if (!x) {
|
||||
return
|
||||
}
|
||||
if (!x) return
|
||||
|
||||
let count = 0
|
||||
|
||||
const filterKeyArray: string[] = []
|
||||
|
||||
filters[x].map((value: string, i: number) => {
|
||||
// first line should be WHERE
|
||||
|
||||
const last = i === filters[x].length - 1
|
||||
|
||||
if (count === 0) {
|
||||
@@ -55,11 +44,8 @@ export function filterSqlWhereBuilder(
|
||||
}
|
||||
})
|
||||
|
||||
// console.log('filterKeyArray', filterKeyArray)
|
||||
whereArray.push(...filterKeyArray)
|
||||
|
||||
// console.log('filters length', filtersSanitized.length)
|
||||
|
||||
// if there are multiple filters in an 'and' sequence
|
||||
// then `and` is inserted between them
|
||||
if (filtersSanitized.length > 0 && i < filtersSanitized.length - 1) {
|
||||
@@ -68,21 +54,12 @@ export function filterSqlWhereBuilder(
|
||||
|
||||
return filterKeyArray
|
||||
})
|
||||
|
||||
// console.log('whereArray', whereArray)
|
||||
|
||||
// console.log('sqlWhereArray', sqlWhereArray)
|
||||
return whereArray
|
||||
}
|
||||
|
||||
export function filterReducer(state: FilterObject, action: any) {
|
||||
// console.log(state, action)
|
||||
|
||||
const oldState = { ...state }
|
||||
|
||||
const newState = { ...oldState, ...action }
|
||||
|
||||
console.log('newState', newState)
|
||||
|
||||
return newState
|
||||
}
|
||||
@@ -21,7 +21,8 @@ export interface LogData {
|
||||
id: string
|
||||
timestamp: number
|
||||
event_message: string
|
||||
metadata: Metadata
|
||||
metadata: Metadata,
|
||||
[other: string]: unknown;
|
||||
}
|
||||
|
||||
export interface LogTemplate {
|
||||
|
||||
@@ -2,33 +2,25 @@ import React, { FC, useEffect, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Dropdown,
|
||||
Typography,
|
||||
IconChevronDown,
|
||||
IconRefreshCw,
|
||||
IconX,
|
||||
Toggle,
|
||||
IconSearch,
|
||||
IconClock,
|
||||
Popover,
|
||||
IconLink,
|
||||
IconExternalLink,
|
||||
IconCalendar,
|
||||
IconEye,
|
||||
Checkbox,
|
||||
Form,
|
||||
} from '@supabase/ui'
|
||||
import { LogSearchCallback, LogTemplate } from '.'
|
||||
import dayjs from 'dayjs'
|
||||
import utc from 'dayjs/plugin/utc'
|
||||
import { FILTER_OPTIONS, LogsTableName } from './Logs.constants'
|
||||
import { LogsFilter } from './Logs.filter'
|
||||
import { DatePicker } from 'components/ui/DatePicker'
|
||||
import DatePickers from './Logs.DatePickers'
|
||||
|
||||
interface Props {
|
||||
defaultSearchValue?: string
|
||||
defaultToValue?: string
|
||||
defaultFromValue?: string
|
||||
templates?: any
|
||||
isLoading: boolean
|
||||
isCustomQuery: boolean
|
||||
@@ -42,10 +34,6 @@ interface Props {
|
||||
dispatchWhereFilters: (x: any) => void
|
||||
whereFilters: any
|
||||
table: LogsTableName
|
||||
te: { value: string; error: string }
|
||||
ts: { value: string; error: string }
|
||||
setTo: ({}) => void
|
||||
setFrom: ({}) => void
|
||||
}
|
||||
|
||||
dayjs.extend(utc)
|
||||
@@ -69,10 +57,6 @@ const PreviewFilterPanel: FC<Props> = ({
|
||||
dispatchWhereFilters,
|
||||
whereFilters: filters,
|
||||
table,
|
||||
// te: to,
|
||||
// ts: from,
|
||||
// setTo,
|
||||
// setFrom,
|
||||
}) => {
|
||||
const [search, setSearch] = useState('')
|
||||
|
||||
@@ -189,8 +173,7 @@ const PreviewFilterPanel: FC<Props> = ({
|
||||
onSubmit={(e) => {
|
||||
// prevent redirection
|
||||
e.preventDefault()
|
||||
setSearch(localSearchValue)
|
||||
handleSearch(localSearchValue)
|
||||
handleSearch()
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
@@ -200,7 +183,7 @@ const PreviewFilterPanel: FC<Props> = ({
|
||||
onChange={(e) => setlocalSearchValue(e.target.value)}
|
||||
onBlur={(e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setSearch(e.target.value)
|
||||
handleSearch(localSearchValue)
|
||||
handleSearch()
|
||||
}}
|
||||
icon={
|
||||
<div className="text-scale-900">
|
||||
@@ -211,7 +194,7 @@ const PreviewFilterPanel: FC<Props> = ({
|
||||
actions={
|
||||
hasEdits && (
|
||||
<button
|
||||
onClick={() => handleSearch(localSearchValue)}
|
||||
onClick={() => handleSearch()}
|
||||
className="text-scale-1100 hover:text-scale-1200 mx-2"
|
||||
>
|
||||
{'↲'}
|
||||
@@ -234,17 +217,14 @@ const PreviewFilterPanel: FC<Props> = ({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{Object.values(FILTER_OPTIONS[table]).map((x) => {
|
||||
// console.log('filter option', x)
|
||||
return (
|
||||
<LogsFilter
|
||||
key={`${x.key}-filter`}
|
||||
options={x}
|
||||
dispatchFilters={dispatchWhereFilters}
|
||||
filtersState={filters}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{Object.values(FILTER_OPTIONS[table]).map((x) => (
|
||||
<LogsFilter
|
||||
key={`${x.key}-filter`}
|
||||
options={x}
|
||||
dispatchFilters={dispatchWhereFilters}
|
||||
filtersState={filters}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import useSWR from 'swr'
|
||||
import React, { useEffect, useReducer, useState } from 'react'
|
||||
import { NextPage } from 'next'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -9,38 +8,24 @@ import {
|
||||
IconAlertCircle,
|
||||
IconRewind,
|
||||
Button,
|
||||
IconInfo,
|
||||
Card,
|
||||
Input,
|
||||
} from '@supabase/ui'
|
||||
|
||||
import { withAuth } from 'hooks'
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { SettingsLayout } from 'components/layouts/'
|
||||
import CodeEditor from 'components/ui/CodeEditor'
|
||||
import {
|
||||
LogTable,
|
||||
LogEventChart,
|
||||
Count,
|
||||
Logs,
|
||||
LogTemplate,
|
||||
TEMPLATES,
|
||||
LogData,
|
||||
LogSearchCallback,
|
||||
LOG_TYPE_LABEL_MAPPING,
|
||||
genDefaultQuery,
|
||||
genCountQuery,
|
||||
LogsTableName,
|
||||
filterSqlWhereBuilder,
|
||||
FilterObject,
|
||||
filterReducer,
|
||||
QueryType,
|
||||
} from 'components/interfaces/Settings/Logs'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite'
|
||||
import { isUndefined } from 'lodash'
|
||||
import dayjs from 'dayjs'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import useLogsPreview from 'hooks/analytics/useLogsPreview'
|
||||
import PreviewFilterPanel from 'components/interfaces/Settings/Logs/PreviewFilterPanel'
|
||||
|
||||
@@ -125,7 +110,7 @@ export const LogPage: NextPage = () => {
|
||||
}
|
||||
|
||||
const handleSearch: LogSearchCallback = ({ query, to, from, fromMicro, toMicro }) => {
|
||||
let toValue, fromValue
|
||||
let toValue
|
||||
if (to || toMicro) {
|
||||
toValue = toMicro ? toMicro : dayjs(to).valueOf() * 1000
|
||||
|
||||
@@ -196,10 +181,6 @@ export const LogPage: NextPage = () => {
|
||||
dispatchWhereFilters={dispatchWhereFilters}
|
||||
whereFilters={whereFilters}
|
||||
table={table}
|
||||
te={te}
|
||||
ts={ts}
|
||||
setTo={setTo}
|
||||
setFrom={setFrom}
|
||||
/>
|
||||
{/* {showChart && (
|
||||
<div>
|
||||
@@ -223,7 +204,7 @@ export const LogPage: NextPage = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<LogTable data={logData} isCustomQuery={false} queryType={type} />
|
||||
<LogTable data={logData} isCustomQuery={false} queryType={type as QueryType} />
|
||||
<div className="p-2">
|
||||
<Button onClick={() => loadOlder()} icon={<IconRewind />} type="default">
|
||||
Load older
|
||||
|
||||
@@ -1,45 +1,15 @@
|
||||
import useSWR from 'swr'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { NextPage } from 'next'
|
||||
import { useRouter } from 'next/router'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import {
|
||||
Typography,
|
||||
IconLoader,
|
||||
IconAlertCircle,
|
||||
IconRewind,
|
||||
Button,
|
||||
IconInfo,
|
||||
Card,
|
||||
Input,
|
||||
} from '@supabase/ui'
|
||||
import { Typography, IconLoader, IconAlertCircle, Button, Card, Input } from '@supabase/ui'
|
||||
|
||||
import { withAuth } from 'hooks'
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { SettingsLayout } from 'components/layouts/'
|
||||
import CodeEditor from 'components/ui/CodeEditor'
|
||||
import {
|
||||
LogPanel,
|
||||
LogTable,
|
||||
LogEventChart,
|
||||
Count,
|
||||
Logs,
|
||||
LogTemplate,
|
||||
TEMPLATES,
|
||||
LogData,
|
||||
LogSearchCallback,
|
||||
LOG_TYPE_LABEL_MAPPING,
|
||||
genDefaultQuery,
|
||||
genCountQuery,
|
||||
LogsTableName,
|
||||
} from 'components/interfaces/Settings/Logs'
|
||||
import { LogPanel, LogTable, LogTemplate, TEMPLATES } from 'components/interfaces/Settings/Logs'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite'
|
||||
import { isUndefined } from 'lodash'
|
||||
import dayjs from 'dayjs'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import useLogsPreview from 'hooks/analytics/useLogsPreview'
|
||||
import useLogsQuery from 'hooks/analytics/useLogsQuery'
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,20 +32,20 @@ test('dedupes log lines with exact id', async () => {
|
||||
id: 'some-uuid',
|
||||
timestamp: 1621323232312,
|
||||
event_message: 'some event happened',
|
||||
metadata: {}
|
||||
metadata: {},
|
||||
},
|
||||
{
|
||||
id: 'some-uuid',
|
||||
timestamp: 1621323232312,
|
||||
event_message: 'some event happened',
|
||||
metadata: {}
|
||||
metadata: {},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)
|
||||
|
||||
// should only have one element, this line will fail if there are >1 element
|
||||
await waitFor(() => screen.getByText(/happened/))
|
||||
await screen.findByText(/happened/)
|
||||
})
|
||||
|
||||
test('can display custom columns and headers based on data input', async () => {
|
||||
|
||||
@@ -72,7 +72,7 @@ beforeEach(() => {
|
||||
useRouter.mockReset()
|
||||
useRouter.mockReturnValue(defaultRouterMock())
|
||||
})
|
||||
test('can display log data and metadata', async () => {
|
||||
test('can display log data', async () => {
|
||||
get.mockResolvedValue({
|
||||
result: [
|
||||
logDataFixture({
|
||||
|
||||
+5
-16
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -17,16 +13,9 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.js",
|
||||
"**/*.jsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in new issue
Block a user