Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/studio/components/interfaces/Connect/Connect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { ExternalLink, Plug } from 'lucide-react'
import { parseAsBoolean, useQueryState } from 'nuqs'
import { useState, useMemo } from 'react'
import { useMemo, useState } from 'react'

import { DatabaseConnectionString } from 'components/interfaces/Connect/DatabaseConnectionString'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import Panel from 'components/ui/Panel'
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from 'lib/constants'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export const DatabaseConnectionString = () => {
</SelectContent_Shadcn_>
</Select_Shadcn_>
</div>
<DatabaseSelector buttonProps={{ size: 'small' }} />
<DatabaseSelector portal={false} buttonProps={{ size: 'small' }} />
</div>

{isLoading && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ export const filterFields = [
return (
<div className="flex w-full items-center justify-between gap-2 font-mono">
<span className="capitalize text-foreground/70 group-hover:text-accent-foreground text-xs">
{props.label}
{props.label.replace('_', ' ')}
</span>
<span className="text-xs text-muted-foreground/70">{props.value}</span>
{/* [Joshen] Temporarily hiding, this feels excessive */}
{/* <span className="text-xs text-muted-foreground/70">{props.value}</span> */}
</div>
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ const buildQueryConditions = (search: QuerySearchParamsType) => {

// Handle scalar values
if (value !== null && value !== undefined) {
whereConditions.push(`${key} = '${value}'`)
if (['host', 'pathname'].includes(key)) {
whereConditions.push(`${key} LIKE '%${value}%'`)
} else {
whereConditions.push(`${key} = '${value}'`)
}
}
})

Expand Down Expand Up @@ -234,7 +238,7 @@ const getEdgeLogsQuery = () => {
WHEN edge_logs_response.status_code >= 500 THEN 'error'
ELSE 'success'
END as level,
edge_logs_request.path as path,
edge_logs_request.path as pathname,
edge_logs_request.host as host,
null as event_message,
edge_logs_request.method as method,
Expand Down Expand Up @@ -273,7 +277,7 @@ const getPostgrestLogsQuery = () => {
WHEN edge_logs_response.status_code >= 500 THEN 'error'
ELSE 'success'
END as level,
edge_logs_request.path as path,
edge_logs_request.path as pathname,
edge_logs_request.host as host,
null as event_message,
edge_logs_request.method as method,
Expand Down Expand Up @@ -311,7 +315,7 @@ const getPostgresLogsQuery = () => {
WHEN pgl_parsed.error_severity = 'ERROR' THEN 'error'
ELSE null
END as level,
null as path,
null as pathname,
null as host,
event_message as event_message,
null as method,
Expand Down Expand Up @@ -341,7 +345,7 @@ const getEdgeFunctionLogsQuery = () => {
WHEN fel_response.status_code >= 500 THEN 'error'
ELSE 'success'
END as level,
fel_request.url as path,
fel_request.url as pathname,
fel_request.host as host,
COALESCE(function_logs_agg.last_event_message, '') as event_message,
fel_request.method as method,
Expand Down Expand Up @@ -387,7 +391,7 @@ const getAuthLogsQuery = () => {
WHEN el_in_al_response.status_code >= 500 THEN 'error'
ELSE 'success'
END as level,
el_in_al_request.path as path,
el_in_al_request.path as pathname,
el_in_al_request.host as host,
null as event_message,
el_in_al_request.method as method,
Expand Down Expand Up @@ -428,7 +432,7 @@ const getSupavisorLogsQuery = () => {
WHEN LOWER(svl_metadata.level) = 'warn' OR LOWER(svl_metadata.level) = 'warning' THEN 'warning'
ELSE 'success'
END as level,
null as path,
null as pathname,
null as host,
null as event_message,
null as method,
Expand Down Expand Up @@ -478,7 +482,7 @@ SELECT
log_type,
status,
level,
path,
pathname,
host,
event_message,
method,
Expand All @@ -498,8 +502,9 @@ ${finalWhere}
* Also returns facets for all filter dimensions
*/
export const getLogsCountQuery = (search: QuerySearchParamsType): string => {
// Use the buildQueryConditions helper
const { finalWhere } = buildQueryConditions(search)
const methodWhere =
finalWhere.length > 0 ? `${finalWhere} AND method is NOT NULL` : 'WHERE method IS NOT NULL'

// Create a count query using the same unified logs CTE
const sql = `
Expand Down Expand Up @@ -530,8 +535,7 @@ UNION ALL
-- Get counts by method
SELECT 'method' as dimension, method as value, COUNT(*) as count
FROM unified_logs
${finalWhere}
WHERE method IS NOT NULL
${methodWhere}
GROUP BY method
`

Expand Down
32 changes: 26 additions & 6 deletions apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import { FilterSideBar } from 'components/ui/DataTable/FilterSideBar'
import { LiveButton } from 'components/ui/DataTable/LiveButton'
import { LiveRow } from 'components/ui/DataTable/LiveRow'
import { DataTableProvider } from 'components/ui/DataTable/providers/DataTableProvider'
import { RefreshButton } from 'components/ui/DataTable/RefreshButton'
import { TimelineChart } from 'components/ui/DataTable/TimelineChart'
import { useUnifiedLogsChartQuery } from 'data/logs/unified-logs-chart-query'
import { useUnifiedLogsCountQuery } from 'data/logs/unified-logs-count-query'
Expand All @@ -45,6 +44,7 @@ import {
TabsList_Shadcn_ as TabsList,
TabsTrigger_Shadcn_ as TabsTrigger,
} from 'ui'
import { RefreshButton } from '../../ui/DataTable/RefreshButton'
import { UNIFIED_LOGS_COLUMNS } from './components/Columns'
import { MemoizedDataTableSheetContent } from './components/DataTableSheetContent'
import { FunctionLogsTab } from './components/FunctionLogsTab'
Expand Down Expand Up @@ -100,19 +100,36 @@ export const UnifiedLogs = () => {
isLoading,
isFetching,
hasNextPage,
refetch,
refetch: refetchLogs,
fetchNextPage,
fetchPreviousPage,
} = useUnifiedLogsInfiniteQuery({ projectRef, search: searchParameters })
const { data: counts, isLoading: isLoadingCounts } = useUnifiedLogsCountQuery({
const {
data: counts,
isLoading: isLoadingCounts,
isFetching: isFetchingCounts,
refetch: refetchCounts,
} = useUnifiedLogsCountQuery({
projectRef,
search: searchParameters,
})
const { data: unifiedLogsChart = [] } = useUnifiedLogsChartQuery({
const {
data: unifiedLogsChart = [],
isFetching: isFetchingCharts,
refetch: refetchCharts,
} = useUnifiedLogsChartQuery({
projectRef,
search: searchParameters,
})

const refetchAllData = () => {
refetchLogs()
refetchCounts()
refetchCharts()
}

const isRefetchingData = isFetching || isFetchingCounts || isFetchingCharts

const rawFlatData = useMemo(() => {
return unifiedLogsData?.pages?.flatMap((page) => page.data ?? []) ?? []
}, [unifiedLogsData?.pages])
Expand Down Expand Up @@ -275,10 +292,13 @@ export const UnifiedLogs = () => {
<FilterSideBar />
<div className="flex max-w-full flex-1 flex-col border-border sm:border-l overflow-hidden">
<DataTableHeaderLayout setTopBarHeight={setTopBarHeight}>
<DataTableFilterCommand searchParamsParser={SEARCH_PARAMS_PARSER} />
<DataTableFilterCommand
placeholder="Search logs..."
searchParamsParser={SEARCH_PARAMS_PARSER}
/>
<DataTableToolbar
renderActions={() => [
<RefreshButton key="refresh" onClick={refetch} />,
<RefreshButton isLoading={isRefetchingData} onRefresh={refetchAllData} />,
fetchPreviousPage ? (
<LiveButton
key="live"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ParserBuilder } from 'nuqs'
import { useEffect, useMemo, useRef, useState } from 'react'

import { useLocalStorage } from 'hooks/misc/useLocalStorage'
import { useHotKey } from 'hooks/ui/useHotKey'
import {
cn,
Command_Shadcn_ as Command,
Expand Down Expand Up @@ -33,9 +32,13 @@ import {
interface DataTableFilterCommandProps {
// TODO: maybe use generics for the parser
searchParamsParser: Record<string, ParserBuilder<any>>
placeholder?: string
}

export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCommandProps) {
export function DataTableFilterCommand({
searchParamsParser,
placeholder = 'Search data table...',
}: DataTableFilterCommandProps) {
const { table, isLoading, filterFields: _filterFields, getFacetedUniqueValues } = useDataTable()
const columnFilters = table.getState().columnFilters
const inputRef = useRef<HTMLInputElement>(null)
Expand All @@ -59,7 +62,8 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo

const trimmedInputValue = inputValue.trim()

useHotKey(() => setOpen((open) => !open), 'k')
// [Joshen] Temporarily disabling as this conflicts with our current CMD K behaviour
// useHotKey(() => setOpen((open) => !open), 'k')

useEffect(() => {
// TODO: we could check for ARRAY_DELIMITER or SLIDER_DELIMITER to auto-set filter when typing
Expand Down Expand Up @@ -139,13 +143,15 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo
trimmedInputValue ? 'text-foreground' : 'text-foreground-light'
)}
>
{trimmedInputValue ? inputValue : 'Search data table...'}
{trimmedInputValue ? inputValue : placeholder}
</span>
<Kbd className="ml-auto text-muted-foreground group-hover:text-accent-foreground">
{/* [Joshen] Temporarily disabling as this conflicts with existing CMD K shortcut */}
{/* <Kbd className="ml-auto text-muted-foreground group-hover:text-accent-foreground">
<span className="mr-1">⌘</span>
<span>K</span>
</Kbd>
</Kbd> */}
</button>

<Command
className={cn(
'overflow-visible rounded-lg border border-border shadow-md [&>div]:border-none bg',
Expand Down Expand Up @@ -185,7 +191,7 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo
const word = getWordByCaretPosition({ value, caretPosition })
setCurrentWord(word)
}}
placeholder="Search data table..."
placeholder={placeholder}
className="text-foreground"
/>
<div className="relative">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@ export function DataTableFilterResetButton<TData>({ value: _value }: DataTableFi
const value = _value as string
const column = table.getColumn(value)
const filterValue = columnFilters.find((f) => f.id === value)?.value

// TODO: check if we could useMemo
const filters = filterValue ? (Array.isArray(filterValue) ? filterValue : [filterValue]) : []

if (filters.length === 0) return null

return (
<Button
type="outline"
className="h-5 rounded-full px-1.5 py-1 font-mono text-[10px]"
icon={<X />}
className="h-5 rounded-full px-1.5 py-1 font-mono text-[10px] [&>span]:-translate-y-[0.6px] space-x-1"
onClick={(e) => {
e.stopPropagation()
column?.setFilterValue(undefined)
Expand All @@ -29,13 +28,8 @@ export function DataTableFilterResetButton<TData>({ value: _value }: DataTableFi
column?.setFilterValue(undefined)
}
}}
icon={<X className="h-2.5 w-2.5 text-muted-foreground" />}
asChild
>
{/* REMINDER: `AccordionTrigger` is also a button(!) and we get Hydration error when rendering button within button */}
<div role="button" tabIndex={0}>
<span>{filters.length}</span>
</div>
{filters.length}
</Button>
)
}
11 changes: 4 additions & 7 deletions apps/studio/components/ui/DataTable/RefreshButton.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import { LoaderCircle, RefreshCcw } from 'lucide-react'
import { Button } from 'ui'

import { useDataTable } from './providers/DataTableProvider'

interface RefreshButtonProps {
onClick: () => void
isLoading: boolean
onRefresh: () => void
}

export function RefreshButton({ onClick }: RefreshButtonProps) {
const { isLoading } = useDataTable()

export const RefreshButton = ({ isLoading, onRefresh }: RefreshButtonProps) => {
return (
<Button
size="tiny"
type="outline"
disabled={isLoading}
onClick={onClick}
onClick={onRefresh}
className="w-[26px]"
icon={
isLoading ? (
Expand Down
4 changes: 3 additions & 1 deletion apps/studio/components/ui/DatabaseSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ interface DatabaseSelectorProps {
buttonProps?: ButtonProps
onSelectId?: (id: string) => void // Optional callback
onCreateReplicaClick?: () => void
portal?: boolean
}

const DatabaseSelector = ({
Expand All @@ -45,6 +46,7 @@ const DatabaseSelector = ({
onSelectId = noop,
buttonProps,
onCreateReplicaClick = noop,
portal = true,
}: DatabaseSelectorProps) => {
const router = useRouter()
const { ref: projectRef } = useParams()
Expand Down Expand Up @@ -110,7 +112,7 @@ const DatabaseSelector = ({
</Button>
</div>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ className="p-0 w-64" side="bottom" align="end" portal={true}>
<PopoverContent_Shadcn_ className="p-0 w-64" side="bottom" align="end" portal={portal}>
<Command_Shadcn_>
<CommandList_Shadcn_>
{additionalOptions.length > 0 && (
Expand Down
2 changes: 1 addition & 1 deletion apps/studio/data/logs/unified-logs-infinite-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ async function getUnifiedLogs(
status: row.status || 200,
method: row.method,
host: row.host,
pathname: (row.url || '').replace(/^https?:\/\/[^\/]+/, '') || row.path || '',
pathname: (row.url || '').replace(/^https?:\/\/[^\/]+/, '') || row.pathname || '',
event_message: row.event_message || row.body || '',
headers:
typeof row.headers === 'string' ? JSON.parse(row.headers || '{}') : row.headers || {},
Expand Down
Loading